Where and Group By Using Moongose API MongoDB - 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 }

Related

How to combine results in a Mongo aggregation query

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" }
}
}
]);

Count Number of objects present on an array under Specific MongoDB ID

My MongoDB Data Structure is like this
{
"_id" : ObjectId("5a3a67b102d9d926f8cd66b8"),
"createdate" : ISODate("2017-12-20T13:37:53.921Z"),
"groupmember" : [
{
"membername" : "a",
"memberid" : "5a20ee1acdacc7086ce7742c"
},
{
"membername" : "b",
"memberid" : "5a20eb5bcdacc7086ce77427"
},
{
"membername" : "c",
"memberid" : "5a20ee35cdacc7086ce7742d"
},
{
"membername" : "d",
"memberid" : "5a20ee67cdacc7086ce7742e"
},
{
"membername" : "e",
"memberid" : "5a20ee9acdacc7086ce7742f"
}
],
"__v" : 0
}
Now I want to calculate the total Number of member present on Group member Array.
I have a solution but it will not for Any specific id.
What I have Solution is like this
Test.aggregate([
{$group:{'_id': '$_id', 'total': { $sum: { $size:"$groupmember" } }}}
], function(error, data){
if (error) {
console.log(error)
} else {
console.log(data);
}
});
So this approach returns all count of the database.
I also tried Match and unwind But it returns blank array. like this
Test.aggregate([
{$match: {_id: '5a3a67b102d9d926f8cd66b8'}},
{$unwind: "$groupmember"},
{$group:{'_id': '$_id', 'total': { $sum: { $size:"$groupmember" } }}}
], function(error, data){
if (error) {
console.log(error)
} else {
console.log(data);
}
});
You can do like:
Test.aggregate(
[
{
$project: {
noOfGroupMembers: { $size: "$groupmember" }
}
}
]
)
If you want to do it with id:
Test.aggregate(
[
{
$match:"5a3a67b102d9d926f8cd66b8"
},
{
$project: {
noOfGroupMembers: { $size: "$groupmember" }
}
}
]
)
Test.find({_id: '5a3a67b102d9d926f8cd66b8'}, function(err, data){
if(err){
console.log(err)
} else {
var length = data[0].groupmember.length;
console.log(length);
}
});

I need help in this query mongodb with nodejs

The query is returning double value, can you help me with this?
the problem stay in unwind ?
is a consultation that consolidates the debits and credits of the db...
with aggregate...
Code:
My DAO ( MongoDB + NodeJS ) :
ListarDebitosDAO.prototype.consolidado = function(usuario,res,req){
this._connection.open(function(err, mongoclient){
mongoclient.collection("contas", function(err, collection){
collection.aggregate([{
$match : {usuario : usuario}},{
$unwind: "$debitos"
},{
$unwind: "$creditos"
}, {
$group: {
"_id" : usuario,
"debitos": {
$sum: "$debitos.debito"
},
"creditos" : {
$sum: "$creditos.credito"
}
}
}])
.toArray(function(error, results){
console.log(results)
var debitoConsolidado = results[0].debitos;
var creditoConsolidado = results[0].creditos;
res.render('dashboard', {debitoConsolidado,nome_usuario: req.session.nome,creditoConsolidado})
});
// mongoclient.close();
});
});
}
My collection ( exemple ) :
{
"_id" : ObjectId("5a09113b42e54fb49230fa37"),
"usuario" : "sara",
"creditos" : [
{
"nome_do_credito" : "credito inicial",
"credito" : 0
},
{
"nome_do_credito" : "salario",
"credito" : 1200
}
],
"debitos" : [
{
"nome_do_debito" : "debito inicial",
"debito" : 0
},
{
"nome_do_debito" : "Vivo",
"debito" : 200
},
{
"nome_do_debito" : "Vivo",
"debito" : 600
}
]
}
My atual output:
{ "_id" : null, "debitos" : 1600, "creditos" : 3600 }
I need :
{ "_id" : null, "debitos" : 800, "creditos" : 1200 }
enter code here
The problem is that you are trying to use both unwind simultaneously, it is creating duplicate entries hence the sum of credit and debit is coming wrong.
I guess what you can do is do sequential unwind and grouping for both credit and debit
collection.aggregate([{
$match : {usuario : usuario}},{
$unwind: "$debitos"
}, {
$group: {
"_id" : usuario,
"debitos": {
$sum: "$debitos.debito"
},
"creditos" : "$creditos"
}
},
{
$unwind: "$creditos"
},
{
$group: {
"_id" : usuario,
"creditos" : {
$sum: "$creditos.credito"
}
"debitos": "$debitos"
}
}
])
I am sure either this or some variant should work for you.
It can be as simple as this:
db.contas.aggregate([
{
$project: {
_id: 1,
usario: 1,
debitos: {
$reduce: {
input: "$debitos",
initialValue: 0,
in: { $add: [ "$$value", "$$this.debito" ] }
}
},
creditos: {
$reduce: {
input: "$creditos",
initialValue: 0,
in: { $add: [ "$$value", "$$this.credito" ] }
}
}
}
}
]);
You can use just one projection and reduce the arrays by summing their members.

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 "."

MongoDB aggregate using distinct

I have an aggregation that groups on a date and creates a sum.
db.InboundWorkItems.aggregate({
$match: {
notificationDate: {
$gte: ISODate("2013-07-18T04:00:00Z")
},
dropType: 'drop'
}
}, {
$group: {
_id: {
notificationDate: "$notificationDate"
},
nd: {
$first: "$notificationDate"
},
count: {
$sum: 1
}
}
}, {
$sort: {
nd: 1
}
})
The output is
"result" : [
{
"_id" : {
"notificationDate" : ISODate("2013-07-18T04:00:00Z")
},
"nd" : ISODate("2013-07-18T04:00:00Z"),
"count" : 484
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-19T04:00:00Z")
},
"nd" : ISODate("2013-07-19T04:00:00Z"),
"count" : 490
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-20T04:00:00Z")
},
"nd" : ISODate("2013-07-20T04:00:00Z"),
"count" : 174
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-21T04:00:00Z")
},
"nd" : ISODate("2013-07-21T04:00:00Z"),
"count" : 6
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-22T04:00:00Z")
},
"nd" : ISODate("2013-07-22T04:00:00Z"),
"count" : 339
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-23T04:00:00Z")
},
"nd" : ISODate("2013-07-23T04:00:00Z"),
"count" : 394
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-24T04:00:00Z")
},
"nd" : ISODate("2013-07-24T04:00:00Z"),
"count" : 17
}
],
"ok" : 1
so far so good. What I need to do now is to keep this, but also add a distinct in the criteria (for argument's sake I want to use AccountId). The would yield me the count of the grouped dates only using distinct AccountId. Is distinct even possible within the aggregation framework?
you can use two group commands in the pipeline, the first to group by accoundId, followed by second group that does usual operation. something like this:
db.InboundWorkItems.aggregate(
{$match: {notificationDate: {$gte: ISODate("2013-07-18T04:00:00Z")}, dropType:'drop' }},
{$group: {_id:"accountId",notificationDate:"$notificationDate"}},
{$group: {_id:1, nd: {$first:"$notificationDate"}, count:{$sum:1} }},
{$sort:{nd:1}} )
db.InboundWorkItems.aggregate({
$match: {
notificationDate: {
$gte: ISODate("2013-07-18T04:00:00Z")
},
dropType: 'drop'
}
}, {
$group: {
_id: "$AccountId",
notificationDate: {
$max: "$notificationDate"
},
dropType: {
$max: "$dropType"
}
}
}, {
$group: {
_id: {
notificationDate: "$notificationDate"
},
nd: {
$first: "$notificationDate"
},
count: {
$sum: 1
}
}
}, {
$sort: {
nd: 1
}
})
I think you might actually be looking for a single group (English is a bit confusing) like so:
db.InboundWorkItems.aggregate({
$match: {
notificationDate: {
$gte: ISODate("2013-07-18T04:00:00Z")
},
dropType: 'drop'
}
}, {
$group: {
_id: {
notificationDate: "$notificationDate", accountId: '$accountId'
},
nd: {
$first: "$notificationDate"
},
count: {
$sum: 1
}
}
}, {
$sort: {
nd: 1
}
})
I add the compound _id in the $group because of:
The would yield me the count of the grouped dates only using distinct AccountId.
Which makes me think you want the grouped date count by account ID.