How to combine results in a Mongo aggregation query - mongodb

I'm new to aggregation queries in Mongo and been really struggling trying to produce the output I want. I have the following aggregation query:
db.events.aggregate([
{ $match: { requestState: "APPROVED" } },
{ $unwind: { path: "$payload.description" } },
{ $group: { _id: { instr: "$payload.description", bu: "$createdByUser", count: { $sum: 1 } } } }
]);
that returns the following results:
{ "_id" : { "instr" : "ABC-123", "bu" : "BU2", "count" : 1 } }
{ "_id" : { "instr" : "ABC-123", "bu" : "BU1", "count" : 1 } }
{ "_id" : { "instr" : "DEF-456", "bu" : "BU1", "count" : 1 } }
How can I amend the aggregation query so that there are only 2 documents returned instead of 3? With the two "ABC-123" results combined into a single result with a new array of counts with the "bu" and "count" fields i.e.
{ "_id" : { "instr" : "ABC-123", "counts": [ { "bu" : "BU1", "count" : 1 }, { "bu" : "BU2", "count" : 1 } ] } }
Many thanks

You can add another stage to only $group by _id.instr and another stage to $project to your desired output shape
db.events.aggregate([
{
$match: { requestState: "APPROVED" }
},
{
$unwind: { path: "$payload.description" }
},
{
$group: {
_id: { instr: "$payload.description", bu: "$createdByUser", count: { $sum: 1 } }
}
},
{
$group: {
_id: { instr: "$_id.instr" },
counts: { $push: { bu: "$_id.bu", count: "$_id.count" } }
}
},
{
$project: {
_id: { instr: "$_id.instr", counts: "$counts" }
}
}
]);

Related

Out new collection with sequence numeric id after stages in mongodb aggregate

I'm trying to out a collection, after some stages in the MongoDB aggregation pipeline.
the out collection needs to contain an id with sequence numeric id (1,2,3,4 etc...).
db.getCollection('MY_COLLECTION').aggregate([{
$addFields: {
"dataEntries.targetCol1": "$dataEntries.col1",
"dataEntries.targetCol2": "$dataEntries.col2"
}
},
{
$project: {
"_id": 0,
"dataEntries.col1": 0,
"dataEntries.col2": 0,
"dataEntries.col3": 0,
"dataEntries.col4": 0
}
},
{
$unionWith: {
coll: "MY_COLLECTION",
pipeline: [{
$addFields: {
"dataEntries.targetCol1": "$dataEntries.col3",
"dataEntries.targetCol2": "$dataEntries.col4"
}
},
{
$project: {
"_id": 0,
"dataEntries.col1": 0,
"dataEntries.col2": 0,
"dataEntries.col3": 0,
"dataEntries.col4": 0
}
}
]
}
},
{
$out: "test_out"
}
])
The result collection contains an objectId as the id.
/* 1 */
{
"_id" : ObjectId("6239cb749482cf6b13248a80"),
"dataEntries" : {
"targetCol1" : "101-A",
"targetCol2" : "101"
}
}
/* 2 */
{
"_id" : ObjectId("6239cb749482cf6b13248a81"),
"dataEntries" : {
"targetCol1" : "101-B",
"targetCol2" : "101"
}
}
There is a way to set the id to sequence numeric counter for every document?
The expected results:
/* 1 */
{
"_id" : 1,
"dataEntries" : {
"targetCol1" : "101-A",
"targetCol2" : "101"
}
}
/* 2 */
{
"_id" : 2,
"dataEntries" : {
"targetCol1" : "101-B",
"targetCol2" : "101"
}
}
In MongoDB 5.0 it is very simple:
db.collection.aggregate([
{
$setWindowFields: {
sortBy: { _id: 1, },
output: { _id: { $documentNumber: {} } }
}
}
])
In earlier versions, it is a little more to do:
db.collection.aggregate([
{
$group: {
_id: null, data: { $push: "$$ROOT" }
}
},
{
$set: {
data: {
$map: {
input: "$data",
in: {
$mergeObjects: ["$$this",
{ _id: {$add: [{ $indexOfArray: ["$data._id", "$$this._id"] }, 1]} }
]
}
}
}
}
},
{ $unwind: "$data" },
{ $replaceWith: "$data" }
])

Filter keys not in collection

How do we find keys which do not exist in collection.
Given an input list of keys ['3321', '2121', '5647'] , i want to return those that do not exist in the collection :
{ "_id" : { "$oid" : "5e2993b61886a22f400ea319" }, "scrip" : "5647" }
{ "_id" : { "$oid" : "5e2993b61886a22f400ea31a" }, "scrip" : "3553" }
So the expected output is ['3321', '2121']
This aggregation gets the desired output (works with MongoDB version 3.4 or later):
INPUT_ARRAY = ['3321', '2121', '5647']
db.test.aggregate( [
{
$match: {
scrip: {
$in: INPUT_ARRAY
}
}
},
{
$group: {
_id: null,
matches: { $push: "$scrip" }
}
},
{
$project: {
scrips_not_exist: { $setDifference: [ INPUT_ARRAY, "$matches" ] },
_id: 0
}
}
] )
The output:
{ "scrips_not_exist" : [ "3321", "2121" ] }

MongoDB Count Items in array by name

I have documents like this:
{
"_id" : ObjectId("5b3ced158735f1196d73a743"),
"cid" : 1,
"foo" : [
{
"k" : "sport",
"v" : "climbing"
},
{
"k" : "sport",
"v" : "soccer"
},
{
"k" : "sport",
"v" : "soccer"
}
]
}
This Query just return the documents which contains a soccer field.
db.coll.find({foo:{$elemMatch:{ v: "soccer"}} }, {"foo.$" : 1,cid:1})
returns:
{ "_id" : ObjectId("5b3ced158735f1196d73a743"), "cid" : 1, "node" : [ { "k" : "sport", "v" : "climbing" } ] }
But I want to know, how many soccer-Elements are in each returned document. How can I count them?
db.coll.aggregate(
// Pipeline
[
// Stage 1
{
$match: {
foo: {
$elemMatch: {
v: 'soccer'
}
}
}
},
// Stage 2
{
$unwind: {
path: '$foo'
}
},
// Stage 3
{
$project: {
cid: 1,
count: {
$cond: {
if: {
$eq: ['$foo.v', 'soccer']
},
then: {
$sum: 1
},
else: 0
}
}
}
},
// Stage 4
{
$group: {
_id: '$cid',
total_count: {
$sum: '$count'
}
}
}
]
);
You can use below query to $filter and $size the filtered array to count no of matching occurrences.
db.coll.aggregate([
{"$project":{
"cid":1,
"count":{
"$size":{
"$filter":{
"input":"$foo",
"cond":{"$eq":["$$this.v","soccer"]
}
}
}
}
}}
])

Where and Group By Using Moongose API MongoDB

I have written an aggregation function which will count the Message. Based on Message Status.
exports.countRecords = function(req, res) {
Message.aggregate([{
$group: {
_id: "$message_status",
count: {
$sum: 1
}
}
}, {
$sort: {
_id: 1
}
}], function(err, result) {
if (err) {
console.log(err);
return;
}
console.log(result);
res.jsonp(result);
});
};
Now i need to add a condition to count only when the channel_message_ID and channel_thread_Id are equal so i have modified the logic like below:
Message.aggregate([{
$match: {
channel_message_ID: { $eq: ["$channel_message_ID", "$channel_thread_Id"]}
}},
{$group: {
_id: "$message_status",
count: {
$sum: 1
}
},
}, {
$sort: {
_id: 1
}
}], function(err, result) {
if (err) {
console.log(err);
return;
}
console.log(result);
res.jsonp(result);
});
};
It is not at all working. Thanks in Advance.
try below solution:
$project:
Message.aggregate([{
{"$project": {
"channel_message_ID":1,
"channel_thread_Id":1,
"message_status": 1,
"cond": {"$eq":["$channel_message_ID","$channel_thread_Id"]}
}
},
{"$match":{"cond": true}},
{$group: {
_id: "$message_status",
count: {
$sum: 1
}
},
}, {
$sort: {
_id: 1
}
}], function(err, result) {
if (err) {
console.log(err);
return;
}
console.log(result);
res.jsonp(result);
});
You'll need to add a new field to the document at the first stage of the aggregation pipeline, this can be done by using the [$addFields]
(https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/) stage
{ "$addFields" : { "idMatch" : { "$eq" : [ "$channel_message_ID" , "$channel_thread_Id" ] } } }
This will create us a new field on the documents flowing through the pipeline telling us if there is a match on the 2 fields. We can then push this in to a $match stage
{ $match: { "idMatch" : true } }
Then you can run the rest of the $group:
{ "$group" : { "_id" : "$message_status", "count" : { "$sum" : 1 } } }
then sort stage:
{ "$sort" : { "_id" : 1 } }
So for example if we add the following documents to a mongodb collection:
db.test.insertMany([{
message_status: "In Progress",
channel_thread_Id: "A",
channel_message_ID: "A"
},
{
message_status: "Completed",
channel_thread_Id: "A",
channel_message_ID: "A"
},
{
message_status: "In Progress",
channel_thread_Id: "A",
channel_message_ID: "B"
},
{
message_status: "Completed",
channel_thread_Id: "A",
channel_message_ID: "B"
}]);
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("599abef4f0cd60f52d67215a"),
ObjectId("599abef4f0cd60f52d67215b"),
ObjectId("599abef4f0cd60f52d67215c"),
ObjectId("599abef4f0cd60f52d67215d")
]
}
We can then run the following aggregation query:
db.test.aggregate([
{ "$addFields" : { "idMatch" : { "$eq" : [ "$channel_message_ID" , "$channel_thread_Id" ] } } },
{ $match: { "idMatch" : true } },
{ "$group" : { "_id" : "$message_status", "count" : { "$sum" : 1 } } },
{ "$sort" : { "_id" : 1 } }
]);
Which will return the expected results of
{ "_id" : "Completed", "count" : 1 }
{ "_id" : "In Progress", "count" : 1 }

Cant find duplicate values for array part in mongodb

db.school.find({ "merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3" })
this is an example document from school collection of that query:
{
"_id" : ObjectId("57fafasf2323232323232f57682cd42"),
"status" : "wait",
"merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3",
"isValid" : false,
"fields" : { "schoolid" : {
"value" : "2323232",
"detail" : {
"revisedBy" : "teacher",
"revisionDate" : ISODate("2015-06-24T09:22:44.288+0000")
},
"history" : [
]
}}
}
I want to see which has duplcate schoolid. SO i do this:
db.school.aggregate([
{$match:{ "merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3"
{ $group: {
_id: { fields.schoolid.value: "$fields.schoolid.value" },
count: { $sum: 1 }
} },
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);
but it gives error.
a lot of errors for a lot of lines
i tried to do like this
_id: { "fields.schoolid.value": "$fields.schoolid.value" },
or
_id: { 'fields.schoolid.value': "$'fields.schoolid.value'" },
but did not work. ow can i use it?
According to the document you provided, there is no fields field, so the group stage can't work. Your query should be :
db.school.aggregate([
{ $match: { "merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3"}},
{ $group: {
_id: { value: "$fields.schoolid.value" },
count: { $sum: 1 }
} },
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);
Also note that fields.schoolid.value is not a valid fieldname, you need to enclode it in "" or to remove the "."