Say I have the following:
this.aggregate(
{$unwind: "$tags"},
{$match: {tags: {$in: pip.activity.tags}}},
{$group : {_id : '$_id',matches:{$sum:1}}},
{$project: { _id: 0,matches:1}},
{$sort: {matches:-1 }},
callback
);
how would I go about including an additional 'external' objectId field in the results? e.g if I have the following:
var otherField = new ObjectId('xxxxxxx');
this.aggregate(
{$unwind: "$tags"},
{$match: {tags: {$in: pip.activity.tags}}},
{$group : {_id : '$_id',matches:{$sum:1}}},
{$project: { _id: 0,matches:1,otherField:otherField}}, <-- include otherField
{$sort: {matches:-1 }},
callback
);
Is this possible or should I be using a forLoop or MapReduce for this particular step? I'm looking for something really efficient.
The $project pipeline operator would not let you inject the object, but you can probably insert the object id earlier in the $group operator. If you have a collection:
db.foo.save({_id:1,tags:['a','b']})
db.foo.save({_id:2,tags:['b','c']})
db.foo.save({_id:3,tags:['c','d']})
You can then write:
db.foo.aggregate({
$unwind: "$tags"},{
$match: { tags: {$in: ['b','c'] } }},{
$group: { _id: "$_id", matches: {$sum: 1 }, otherField: {$min: new ObjectId()} }},{
$project: { _id: 0, matches: 1, otherField: 1 }},{
$sort: { matches: -1 }})
The $min or $max can be used here, but it expects an operator or reference to a field so you have to give it one..
Related
We have a collection with multiple documents ordered with respect to a given timestamp. We want to aggregate documents between two timestamps (let's say startTime and stopTime): that is a simple match stage in our aggregation that has a query such as timestamp: {$gte: startTime, $lte: stopTime}. However, we'd like to include two extra documents in the result of this step: the closest document right before startTime, no matter how far back in time we would need to look, and also the closest document right after stopTime. Is there a way to achieve this with the aggregation framework in MongoDB?
One option if you are already after filtering out these documents, is using a $lookup step with a pipeline. It looks a bit clumsy after the $lookups, but I could not think about another way to continue without grouping all the documents, which is not the best way to go.
$match - This is a "fake" step in order to level up with your situation. You already have it in your current pipeline, thus don't need it here
$set the "$$ROOT" in order to use it latter
$lookup twice in order to get your requested documents from the original collection
For each document create an array of documents, in order to get the before and after out of the current documents
$unwind to separate into documents
$group by _id in order to remove the duplicates of the before and after documents
Format
db.collection.aggregate([
{$match: {timestamp: {$gte: startTime, $lte: stopTime}}},
{$set: {data: "$$ROOT"}},
{$lookup: {
from: "collection",
let: {},
pipeline: [
{$match: {timestamp: {$lt: startTime}}},
{$sort: {timestamp: -1}},
{$limit: 1}
],
as: "before"
}},
{$lookup: {
from: "collection",
let: {},
pipeline: [
{$match: {timestamp: {$gt: stopTime}}},
{$sort: {timestamp: 1}},
{$limit: 1}
],
as: "after"
}},
{$project: {_id: 0, data: {$concatArrays: ["$after", "$before", ["$data"]]}}},
{$unwind: "$data"},
{$group: {_id: "$data._id", data: {$first: "$data"}}},
{$replaceRoot: {newRoot: "$data"}},
{$sort: {timestamp: 1}}
])
See how it works on the playground example
Chain up $unionWith with $sort and $limit: 1 to get the documents out of range.
db.collection.aggregate([
{
$match: {
datetime: {
$gte: ISODate("2022-10-18"),
$lte: ISODate("2022-10-19")
}
}
},
{
"$unionWith": {
"coll": "collection",
"pipeline": [
{
$match: {
datetime: {
$lt: ISODate("2022-10-18")
}
}
},
{
$sort: {
datetime: -1
}
},
{
$limit: 1
}
]
}
},
{
"$unionWith": {
"coll": "collection",
"pipeline": [
{
$match: {
datetime: {
$gt: ISODate("2022-10-19")
}
}
},
{
$sort: {
datetime: 1
}
},
{
$limit: 1
}
]
}
}
])
Here is the Mongo Playground for your reference.
I am trying this mongodb aggregation. I got the output but how can I get the count value with all records.
db.STREETLIGHTS.aggregate(
[
{$match : {"CreateDate":{$gt:new Date(ISODate("2018-04-09T23:54:16.064Z") - 24*60*60 * 1000)}}},
{ $project: {_id:1, SLC_ID:1,LONGITUDE:1,LATITUDE:1,DCUID:1,CUMILITIVE_KWH:1,LAMPSTATUS:1,CreateDate:1 } },
]
)
Please try this.
db.STREETLIGHTS.aggregate([
{$match : {"CreateDate":{$gt:new Date(ISODate("2018-04-09T23:54:16.064Z") - 24*60*60 * 1000)}}},
{$project: {_id:1, SLC_ID:1,LONGITUDE:1,LATITUDE:1,DCUID:1,CUMILITIVE_KWH:1,LAMPSTATUS:1,CreateDate:1 } },
{$group: {_id: null, count: {$sum: 1}}}
])
You need to use the $sum operator in the $project stage :
{$project: {
// Your others projections
total: {$sum: 1}
}
I have a list of items, and I want mongoDB return the result of the sum of their price.
Schema = {
_id: ObjectId,
price: Integer,
}
I'm trying using the aggregation framework, but I can't figure out how correctly use it.
Here an example
db.items.aggregate([
{$match: {_id: {$in: [103070,103069]}}},
{$unwind: "$items"},
{$group: {_id: "$items", count: {$sum: "$items.price"}}},
{$project: {_id: 1}}
])
you are not correctly using the $in operator... $in is used to find elements inside an array, in this case you should use $gte (greater than equal), $lte (less then equal)... Your schema simply don't have "nodeId"... $unwind is used when you have an array inside the document what I presume is not the case... If i understand the query should be something like:
db.items.aggregate([
{$match : {_id : { $gte : 103069, $lte : 103070 } } },
{$group: {_id: null, count: {$sum: "$price"}}},
{$project: {_id: 0, count : 1}}
])
I need help with a query in mongo where I want to fetch the posts tags where post status: 'accepted, group the tags and order them by occurrences.
db.collection('Posts').aggregate([{
$project: {
tags: 1
}
}, {
$unwind: "$tags"
}, {
$group: {
_id: {
tag: "$tags"
},
count: {
$sum: 1
}
}
}], function (error, result) {
// do stuff
});
This works but they do not order by that count field and I don't know how to select only posts that have status accepted. Any ideas?
In order to filter posts by status, you can use $match operator. For ordering by count you should use $sort operator.
Updated version of the query should be like :
db.collection('Posts').aggregate([
{$match : {status : "accepted"}},
{$project: {tags: 1}},
{$unwind: "$tags"},
{$group: {_id: {tag: "$tags"},count: {$sum: 1}}},
{$sort : {count : 1}}],
function (error, result) {
// do stuff
});
After piping various documents through the aggregate framework I finally have the resultant documents.
The problem now is that I only want the $first and $last document.
The final document looks like this (huge list):
...
{
"_id" : "Kaila Deibler",
"count" : 406
},
{
"_id" : "Jessika Dagenais",
"count" : 406
},
{
"_id" : "Tamika Schildgen",
"count" : 404
},
...
The mongo shell command that I used to produce the document is:
db.talks.aggregate([{$project: {_id: 0, comments: "$comments"}}, {$unwind: "$comments"}, {$group: {_id: "$comments.author", count: {$sum: 1}}}, {$sort: {count: -1}}])
But I only need the first and last document so I attempted something like this:
db.talks.aggregate([{$project: {_id: 0, comments: "$comments"}}, {$unwind: "$comments"}, {$group: {_id: "$comments.author", count: {$sum: 1}}}, {$sort: {count: -1}}, {$first: "$_id"}])
I tried other implementations but can't seem to figure out when/where to implement $first and $last
Any suggestions?
$first and $last are aggregation functions in the $group pipeline, which means you need to use them inside the $group document.
db.talks.aggregate([{$project: {_id: 0, comments: "$comments"}}, {$unwind: "$comments"}, {$group: {_id: "$comments.author", count: {$sum: 1}, first: {$first:"$_id"}}}, {$sort: {count: -1}}])
Similarly for $last
You can find an example that shows this here
Here is my own answer to this question:
db.talks.aggregate([{$project: {_id: 0, comments: "$comments"}}, {$unwind: "$comments"}, {$group: {_id: "$comments.author", count: {$sum: 1}}}, {$sort: {count: -1}}, {$group: {_id: "$_id.comments.author", first: {$first: "$_id"}, last: {$last: "_id"}}}])
Since I want to pull the first and last document after the sort I have to apply another $group with $first and $last. The result:
{
"result" : [
{
"_id" : null,
"first" : "Elizabet Kleine",
"last" : "Mariela Sherer"
}
],
"ok" : 1
}
Note the "_id" : null. Is this necessary?
If anyone has a better solution please share.