I have the following query...
db.getCollection('apprenticeships')
.aggregate([
{
$match: {
'Vacancy._id': { $in: [1, 2, 3] },
}
},
{
$group: {
'_id': {
'VacancyId': '$Vacancy._id',
'Status': '$Status'
},
'Count': { $sum: 1 }
}
},
{
$sort: {
'_id.VacancyId': 1,
'_id.Status': 1
}
}
])
Which gives results where each element has following structure
{
"_id" : {
"VacancyId" : 1,
"Status" : 90
},
"Count" : 40.0
}
How can I remap that structure so that the elements in the output look like this instead?
{
"VacancyId": 1,
"Status": 90,
"Count": 40
}
You can add $project stage to aggregation pipeline to add new fields VacancyId and status and then hide the _id
db.getCollection('apprenticeships')
.aggregate([{
$match: {
'Vacancy._id': {
$in: [1, 2, 3]
},
}
},
{
$group: {
'_id': {
'VacancyId': '$Vacancy._id',
'Status': '$Status'
},
'Count': {
$sum: 1
}
}
},
{
$sort: {
'_id.VacancyId': 1,
'_id.Status': 1
}
},
{
{
$project:{ 'VacancyId': '$_id.VacancyId', 'Status': '$_id.Status', 'Count': '$Count', '_id': 0 }
}
}
])
Related
I am running the following aggregation pipeline:
const agg = [
{
'$match': {
'aaa': 'bbb'
}
}, {
'$group': {
'_id': '',
'total': {
'$sum': '$num'
}
}
}
];
My problem is, when $match matches nothing, the pipeline returns 0 documents. How do I get the pipeline to always return 1 document?
In MongoDB version 6.0 you can do it like this one:
db.collection.aggregate([
{ $match: { aaa: 'bbb' } },
{
$group: {
_id: null,
total: { $sum: "$num" }
}
},
{
$densify: {
field: "total",
range: { step: 1, bounds: [0, 0] }
}
},
{ $set: { _id: { $cond: [{ $eq: [{ $type: "$_id" }, "missing"] }, MaxKey, "$_id"] } } },
{ $sort: { _id: 1 } },
{ $limit: 1 }
])
In version < 6.0 you can try this one:
db.collection.aggregate([
{
$facet: {
data: [
{ $match: { aaa: 'bbb' } },
{ $group: { _id: null, total: { $sum: "$num" } } }
],
default: [
{ $limit: 1 },
{ $group: { _id: null, total: { $sum: 0 } } },
{ $set: { _id: MaxKey } }
]
}
},
{ $replaceWith: { $mergeObjects: [{ $first: "$default" }, { $first: "$data" }] } },
])
Or this one:
db.collection.aggregate([
{ $match: { aaa: 'bbb' } },
{ $group: { _id: null, total: { $sum: "$num" } } },
{
$unionWith: {
coll: "collection",
pipeline: [
{ $limit: 1 },
{ $set: { _id: MaxKey, total: 0 } },
{ $project: { _id: 1, total: 1 } }
]
}
},
{ $sort: { _id: 1 } },
{ $limit: 1 }
])
I'm running this aggregate function, which is supposed to only show results when they have a count greater than 1. When I remove 'count': { '$gt': 1 } the aggregate works, however it obviously shows all results. How should I use this count correctly?
db.getCollection('songs').aggregate([
{
'$match': { 'is_song': 1, 'is_soundtrack': 0, 'count': { '$gt': 1 } }
},
{
'$group': { '_id': { 'name': '$name', 'artist_id': '$artist_id' }, 'count': { '$sum': 1 } }
},
{
'$sort': { 'count': -1 }
}
])
Sample data:
{
"_id" : ObjectId("5f93a43b4e8883298849ad18"),
"name" : "Come Fly With Me",
"song_id" : 5,
"artist_id" : 5,
"is_song" : 1,
"is_soundtrack" : 0,
"updatedAt" : ISODate("2016-10-04T13:34:53.328Z")
}
You should not add 'count': { '$gt': 1 } in the first $match stage.
As the count field is only populated after the $group stage.
So, you need add another $match stage after $group stage for filtering document with the count value is greater than 1.
db.collection.aggregate([
{
"$match": {
"is_song": 1,
"is_soundtrack": 0
}
},
{
"$group": {
"_id": {
"name": "$name",
"artist_id": "$artist_id"
},
"count": {
"$sum": 1
}
}
},
{
$match: {
"count": {
"$gt": 1
}
}
},
{
"$sort": {
"count": -1
}
}
])
Sample Mongo Playground
I'm trying to return size of 'orders' and sum of 'item' values for each 'order' for each order from documents like the example document:
orders: [
{
order_id: 1,
items: [
{
item_id: 1,
value:100
},
{
item_id: 2,
value:200
}
]
},
{
order_id: 2,
items: [
{
item_id: 3,
value:300
},
{
item_id: 4,
value:400
}
]
}
]
I'm using following aggregation to return them, everything works fine except I can't get size of 'orders' array because after unwind, 'orders' array is turned into an object and I can't call $size on it since it is an object now.
db.users.aggregate([
{
$unwind: "$orders"
},
{
$project: {
_id: 0,
total_values: {
$reduce: {
input: "$orders.items",
initialValue: 0,
in: { $add: ["$$value", "$$this.value"] }
}
},
order_count: {$size: '$orders'}, //I get 'The argument to $size must be an array, but was of type: object' error
}
},
])
the result I expected is:
{order_count:2, total_values:1000} //For example document
{order_count:3, total_values:1500}
{order_count:5, total_values:2500}
I found a way to get the results that I wanted. Here is the code
db.users.aggregate([
{
$project: {
_id: 1, orders: 1, order_count: { $size: '$orders' }
}
},
{ $unwind: '$orders' },
{
$project: {
_id: '$_id', items: '$orders.items', order_count: '$order_count'
}
},
{ $unwind: '$items' },
{
$project: {
_id: '$_id', sum: { $sum: '$items.value' }, order_count: '$order_count'
}
},
{
$group: {
_id: { _id: '$_id', order_count: '$order_count' }, total_values: { $sum: '$sum' }
}
},
])
output:
{ _id: { _id: ObjectId("5dffc33002ef525620ef09f1"), order_count: 2 }, total_values: 1000 }
{ _id: { _id: ObjectId("5dffc33002ef525620ef09f2"), order_count: 3 }, total_values: 1500 }
With this MongoDB aggregation pipeline:
db.getCollection('device1_hour_events').aggregate([
{ $match: { 'ts_hour' : ISODate('2013-10-11T04:00:00.000Z') } },
{ $unwind: '$minutes' },
{ $match: { 'minutes.min': { $gt: -1, $lt: 2 } } },
{ $unwind: '$minutes.seconds' },
{ $group: { '_id': '$minutes.min',
'temp_min': { $min: '$minutes.seconds.temp' },
'temp_avg': { $avg: '$minutes.seconds.temp' },
'temp_max': { $max: '$minutes.seconds.temp' }
}
},
{ $sort: { '_id': 1} }
])
that produces the following result:
/* 1 */
{
"_id": 0,
"temp_min": 12,
"temp_avg": 47.25,
"temp_max": 99
}
/* 2 */
{
"_id": 1,
"temp_min": 35,
"temp_avg": 47.67,
"temp_max": 65
}
It's possible to obtain maybe with $project the following output:
{
"_id": [0, 1],
"temp_min": [12, 35],
"temp_avg": [47.25, 47.67],
"temp_max": [99, 65]
}
You can add another $group with a $push for each field :
{
$group: {
'_id': 0,
'_ids': { $push: '$_id' },
'temp_min': { $push: '$temp_min' },
'temp_avg': { $push: '$temp_avg' },
'temp_max': { $push: '$temp_max' }
}
}
How do I create an aggregate operation that shows me 5 for each type?
For example, what I need is to show 5 of type= 1 , 5 of type=2 and 5 of type=3.
I have tried:
db.items.aggregate([
{$match : { "type" : { $gte:1,$lte:3 }}},
{$project: { "type": 1, "subtipo": 1, "dateupdate": 1, "latide": 1, "long": 1, "view": 1,month: { $month: "$dateupdate" } }},
{$sort:{view: -1, dateupdate: -1}},
{$limit:5}
]);
After the $match pipeline, you need to do an initial group which creates an array of the original documents. After that you can $slice the array with the documents to return the 5 elements.
The intuition can be followed in this example:
db.items.aggregate([
{ '$match' : { 'type': { '$gte': 1, '$lte': 3 } } },
{
'$group': {
'_id': '$type',
'docs': { '$push': '$$ROOT' },
}
},
{
'$project': {
'five_docs': {
'$slice': ['$docs', 5]
}
}
}
])
The above will return the 5 documents unsorted in an array. If you need to return the TOP 5 documents in sorted order then you can introduce a $sort pipeline before grouping the docs that re-orders the documents getting into the $group pipeline by the type and dateupdate fields:
db.items.aggregate([
{ '$match' : { 'type': { '$gte': 1, '$lte': 3 } } },
{ '$sort': { 'type': 1, 'dateupdate': -1 } }, // <-- re-order here
{
'$group': {
'_id': '$type',
'docs': { '$push': '$$ROOT' },
}
},
{
'$project': {
'top_five': {
'$slice': ['$docs', 5]
}
}
}
])