I can nt make a sum in a nested mongo query - mongodb

I tried all weekend to write this query, but it is not working. The nested field worked, but the other field does not show the $sum.
This is a example of the collections:
{
"_id": ObjectId("5dc28a3fd89a4d7bb82a75b4"),
"bedrijf": ObjectId("5c7ee51d2478a30fa4357b4c"),
"doelgroep": "Kinderen 12-",
"recreatieleider": 1,
"__v": 0
}
I like to group the key "bedrijf" and make a subgroup "doelgroep" The code I use is:
{
$group: {
_id: {
bedrijf: "$bedrijf",
doelgroep: "$doelgroep",
},
"totaal": {
$sum: 1
}
}
},
{
$group : {
_id : '$_id.bedrijf',
doelgroep : {
"$push": {
doelgroep:"$_id.doelgroep",
total:"$totaal"
}
},
"recreatieleider": {
$sum: "$recreatieleider"
}
}
}
But when I see the results the key "recreatieleder" does not make the sum of the entries.
{
"results": [
{
"_id": "5c69c5d939fbb38a1fcf3146",
"doelgroep": [
{
"doelgroep": "Gezinnen",
"total": 1
},
{
"doelgroep": "Kinderen 9-11",
"total": 9
}
],
"recreatieleider": 0
}
]
}
So how can I count the "recreatieleider" value?
My final expected output must be:
{
"results": [
{
"_id": "5c69c5d939fbb38a1fcf3146",
"doelgroep": [
{
"doelgroep": "Gezinnen",
"total": 1
},
{
"doelgroep": "Kinderen 9-11",
"total": 9
}
],
"recreatieleider": 20
}
]
}
added question
I ran to a other problem. You make me fixed the first problem. When a mak a other sub group it will not show me
I use this code
{
$group: {
_id: {
bedrijf: "$bedrijf",
doelgroep: "$doelgroep",
},
"totaal": {
$sum: 1
},
"hulpkrachten": {
$sum: "$hulpkrachten"
},
"recreatieleider": {
$sum: "$recreatieleider"
},
"stagiaires": {
$sum: "$stagiaires"
},
}
},
{
$group : {
_id : '$_id.bedrijf',
doelgroep : {
"$push": {
doelgroep:"$_id.doelgroep",
total:"$totaal"
}
},
soortactiviteit : {
"$push": {
soortactiviteit:"$_id.soortactiviteit",
total:"$totaal"
}
}
}
}
You can see I add "soortactiviteit" to it, but it wil not add u subgroup
The expeditor outcome must be
{
"results": [
{
"_id": "5c69c5d939fbb38a1fcf3146",
"doelgroep": [
{
"doelgroep": "Gezinnen",
"total": 1
},
{
"doelgroep": "Kinderen 9-11",
"total": 9
}
],
"soortactivieit": [
{
"doelgroep": "creativiteit",
"total": 20
},
{
"doelgroep": "sports,
"total": 9
}
],
"recreatieleider": 20
}
]
}
Maybe you can help with my last question of the subject.. Thank

Not sure, what you are trying to achieve with this query. However, below query works for your scenario.
{
$group:{
_id:{
bedrijf:"$bedrijf",
doelgroep:"$doelgroep",
},
"totaal":{
$sum:1
},
"recreatieleider":{
$sum:"$recreatieleider"
}
}
},
{
$group:{
_id:'$_id.bedrijf',
doelgroep:{
"$push":{
doelgroep:"$_id.doelgroep",
total:"$totaal"
}
},
"recreatieleider":{
$sum:"$recreatieleider"
}
}
}

When first $group runs the key recreatieleider is lost in
{
$group: {
_id: {
bedrijf: "$bedrijf",
doelgroep: "$doelgroep",
},
"totaal": {
$sum: 1
}
}
},
change it to and try then:
{
$group: {
_id: {
bedrijf: "$bedrijf",
doelgroep: "$doelgroep",
},
"totaal": {
$sum: 1
},
"recreatieleider": {
$sum: "$recreatieleider"
}
}
},

Related

MongoDB : group and count users by gender, civilStatus and professionalCategory

I have a collection of users, each user has a profile. I want to implement a query to make statistics on users.
This is my collection.
[
{
"_id": ObjectId("61d2db0d273a9076d630697b"),
"state": "VALIDATED",
"phone": "xxx",
"civilStatus": "SINGLE",
"gender": "MALE",
"professionalCategory": "STUDENT"
}
]
I want the result to contain an array of all genders of users in the database, and the number of users with each gender. same for civilStatus and professionalCategories
This is the result i am looking for :
{
"total": 2000
"validated": 1800,
"genders": [
{
"value": "MALE",
"count": 1200
},
{
"value": "FEMALE",
"count": 600
}
],
"civilStatus": [
{
"value": "SINGLE",
"count": "300"
}
...
],
"professionalCategories": [
{
"value": "STUDENT",
"count": "250"
}
...
]
}
I implemented the query, but I still have a few things that I don't know how to do.
db.getCollection("users").aggregate([
{
$group: {
_id: null,
validated: {
$sum: {
$cond: {
if: { $eq: ["$state", "VALIDATED"] },
then: 1,
else: 0
}
}
},
genders: {
$push: "$gender"
},
civilStatus: {
$push: "$civilStatus"
},
professionalCategories: {
$push: "$professionalCategory"
}
}
}
])
This is the result of this query :
{
"total": 2000
"validated": 1800,
"genders": [
"MALE",
"MALE",
"FEMALE",
"MALE",
"FEMALE",
"FEMALE"
...
],
"civilStatus": [
"SINGLE",
"MARIED",
"SINGLE",
...
],
"professionalCategories": [
"STUDENT",
"WORKER",
"RETIRED"
...
]
}
I miss how to group each gender, civil Status and professional Category and calculate the number of users for each one.
I also tried this query, but I don't know how to complete the "count" field for each item of the array :
db.getCollection("users").aggregate([
{
$group: {
_id: null,
validated: {
$sum: {
$cond: {
if: { $eq: ["$state", "VALIDATED"] },
then: 1,
else: 0
}
}
},
genders: {
$addToSet: {
value: "$gender",
count: {
//
}
}
},
civilStatus: {
$addToSet: {
value: "$civilStatus",
count: {
//
}
}
},
professionalCategories: {
$addToSet: {
value: "$professionalCategory",
count: {
//
}
}
},
}
}
])
if the query was to treat only one field, for example gender. it would have been easier with "unwind". but here I have 3 fields.
can someone help me please?
You can use following aggregation
Here is the code
db.collection.aggregate([
{
"$facet": {
"genders": [
{
"$group": {
"_id": "$gender",
"total": { $sum: 1 }
}
}
],
"civilStatus": [
{
"$group": {
"_id": "$civilStatus",
"total": { $sum: 1 }
}
}
],
"professionalCategory": [
{
"$group": {
"_id": "$professionalCategory",
"total": { $sum: 1 }
}
}
],
"validated": [
{
"$group": {
"_id": "$state",
"total": { "$sum": 1 }
}
}
]
}
},
{
$set: {
validated: {
"$filter": {
"input": "$validated",
"cond": {
"$eq": [ "$$this._id", "VALIDATED" ]
}
}
}
}
},
{
$set: {
validated: {
"$ifNull": [
{
"$arrayElemAt": [ "$validated", 0 ]
},
0
]
}
}
},
{
$set: { validated: "$validated.total" }
}
])
Working Mongo playground

MongoDB match filters with grouping and get total count

My sample data:
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_2",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_3",
"priority": "P2",
"owners": ["user-1", "user-2"],
},
I want to run an aggregation pipeline on the data involving match filters and grouping, also I want to limit the number of groups returned as well as the number of items in each group.
Essentially, if limit=2, limit_per_group=1, group_by=owner, priority=P1, I want the following results:
[
{
"data": [
{
"group_key": "user-1",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
{
"group_key": "user-2",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
]
},
{
"metadata": {
"total_items_matched": 2,
"total_groups": 2
}
},
]
Need some help on how to write an aggregation pipeline to get the required result.
My current query is as follows:
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 1,
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched"
}
]
}
}
Mongo playground link
I am unable to calculate the total number of groups.
add new stage of $addfields at the end of pipeline
db.collection.aggregate([
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 0,
"group_key": "$_id",
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched",
}
]
}
},
{
"$addFields": {
"metadata.total_groups": {
"$size": "$data"
}
}
}
])
https://mongoplayground.net/p/y5a0jvr6fxI

Mongodb Pagination after aggregate

I want merge metadata and metadatax to display result
properly, as you can see I have used $match multiple times to get
proper result after aggregate.
I also need total_pages in metadata, How to implement this formula Math.ceil(total_items/limit).
Note: The code is working properly but need
to optimize the logic.
db.collection('cms_brands').aggregate([
{
$facet: {
data: [{ $match: {title:"clue brand"} }, { $skip: 0 }, { $limit: 10 }],
metadatax: [ { $count: "total" }, { $addFields: { page: 0 } }],
metadata: [ { $match: {title:"clue brand"} }, { $skip: 0 }, { $limit: 10 }, {$group: { _id: "$title", count: { $sum: 1 } }} ],
}
}
]).toArray()
.then((items) => { return { statusCode: 200, body: items }; })
.catch(err => {
console.log('=> an error occurred: ', err);
return { statusCode: 500, body: 'error' };
});
Actual response :
[
{
"data": [
{
"_id": "5e91d2c4a3fda138bcdbfd82",
"title": "clue brand"
},
{
"_id": "5e91d2dea3fda138bcdbfd83",
"title": "clue brand"
}
],
"metadatax": [
{
"total": 3,
"page": 0
}
],
"metadata": [
{
"_id": "clue brand",
"count": 2
}
]
}
]
Expected response :
[
{
"data": [
{
"_id": "5e91d2c4a3fda138bcdbfd82",
"title": "clue brand"
},
{
"_id": "5e91d2dea3fda138bcdbfd83",
"title": "clue brand"
}
],
"metadata": [
{
"total": 3,
"page": 0,
"_id": "clue brand",
"count": 2
}
]
}
]
You can change few things, try below query :
db.collection.aggregate([
{
$facet: {
metadatax: [{ $count: "total" }, { $addFields: { page: 0 } }],
metadata: [
{ $match: { title: "clue brand" } },
{ $skip: 0 },
{ $limit: 10 },
{
$group: {
_id: "$title",
count: { $sum: 1 },
data: { $push: "$$ROOT" }
}
}
]
}
},
{ $unwind: "$metadata" },
{
$project: {
data: "$metadata.data",
metadata: {
$mergeObjects: [
{ _id: "$metadata._id", count: "$metadata.count",total_pages: { $ceil: { $divide: [ { $arrayElemAt: [ "$metadatax.total", 0 ] } , 8 ] } } },
{ $arrayElemAt: ["$metadatax", 0] }
]
}
}
}
]);
Test : MongoDB-Playground
Updates :
You don't need this data: [{ $match: {title:"clue brand"} }, { $skip: 0 }, { $limit: 10 }] additional step in $facet, can be replaced with data: { $push: "$$ROOT" } in $group of metadata.
Also as metadata will always be an array of single object - instead of being an array, I would suggest it to be an object for easy access in code. Just in case if you need it as an array all you need is to wrap $mergeObjects in array in $project.
Like this :
metadata: [{
$mergeObjects: [
{ _id: "$metadata._id", count: "$metadata.count" },
{ $arrayElemAt: ["$metadatax", 0] }
]
}]

Group on field while getting the last document for each field with MongoDB

Problem
I'm trying to group a stock inventory by products. At first, my stock entries was fully filled each time so I made this aggregate:
[
{ $sort: { date: 1 } },
{
$group: {
_id: '$userId',
stocks: { $last: '$stocks' },
},
},
{ $unwind: '$stocks' },
{
$group: {
_id: '$stocks.productId',
totalQuantity: { $sum: '$stocks.quantity' },
stocks: { $push: { userId: '$_id', quantity: '$stocks.quantity' } },
},
},
]
Now, it can be possible that a stock entry doesn't contain all the products filled. So I'm stuck while writing the new aggregate.
Basically I need to group every products by productId and have an array of the last entry for each user.
Output
This is my expected output:
[
{
"_id": ObjectId("5e75eae1359fc8159d5b6073"),
"totalQuantity": 33,
"stocks": [
{
"userId": ObjectId("5e75f498359fc8159d5b6075"),
"lastDate": "2020-03-21T11:45:53.077Z",
"quantity": 33
}
]
},
{
"_id": ObjectId("5e75eaea359fc8159d5b6074"),
"totalQuantity": 2,
"stocks": [
{
"userId": ObjectId("5e75f498359fc8159d5b6075"),
"lastDate": "2020-03-21T11:45:53.077Z",
"quantity": 2
}
]
}
]
Documents
Documents (when fully filled):
{
"_id": ObjectId("5e75fe71e4a3e0323ba47e0a"),
"date": "2020-03-21T11:45:53.077Z",
"userId": ObjectId("5e75f498359fc8159d5b6075"),
"stocks": [
{
"productId": ObjectId("5e75eae1359fc8159d5b6073"),
"quantity": 33
},
{
"productId": ObjectId("5e75eaea359fc8159d5b6074"),
"quantity": 2
}
]
}
Sometimes it won't be filled for the whole inventory (that's why I need the lastDate):
{
"_id": ObjectId("5e75fe71e4a3e0323ba47e0a"),
"date": "2020-03-21T11:45:53.077Z",
"userId": ObjectId("5e75f498359fc8159d5b6075"),
"stocks": [
{
"productId": ObjectId("5e75eae1359fc8159d5b6073"),
"quantity": 33
}
]
}
Try this one:
db.collection.aggregate([
{
$group: {
_id: "$userId",
root: {
$push: "$$ROOT"
}
}
},
{
$addFields: {
root: {
$map: {
input: "$root",
as: "data",
in: {
"stocks": {
$map: {
input: "$$data.stocks",
as: "stock",
in: {
"productId": "$$stock.productId",
"userId": "$$data.userId",
"quantity": "$$stock.quantity",
"lastDate": "$$data.date"
}
}
}
}
}
}
}
},
{
$unwind: "$root"
},
{
$replaceRoot: {
newRoot: "$root"
}
},
{
$unwind: "$stocks"
},
{
$sort: {
"stocks.lastDate": 1
}
},
{
$group: {
_id: "$stocks.productId",
totalQuantity: {
$last: "$stocks.quantity"
},
stocks: {
$last: "$stocks"
}
}
},
{
$addFields: {
stocks: [
{
"lastDate": "$stocks.lastDate",
"quantity": "$stocks.quantity",
"userId": "$stocks.userId"
}
]
}
}
])
MongoPlayground

What is wrong with this MongoDB Aggregate?

This is my sample document.I have many such documents where Cpe.Srno is a key and Cpe.date keeps getting updated using an Api
{
"_id": "8NH5rNCz47fyoo3z2",
"Cpe": {
"Srno": "GZDBDt2NmpMv54j",
"date": {
"$date": "2016-12-04T14:51:26.452Z"
}
},
"serialNumber": 4.703961341e+09,
"Device": {
"object_id": 1,
"terminal_id": 3,
"Info": {
"FirmwareVersion": {
"value": "5.9.2"
},
"Model": {
"value": "lunus Rustic HRK paxton - 989"
},
"DeviceSerialNumber": {
"value": 3.830919496e+09
},
"BatteryType": {
"value": "Handcrafted lithium battery"
},
"SavedPriority": {
"value": 7
}
}
}
}
I am trying to group by Cpe.Srno and get the latest document using Cpe.date.So i get distinct Cpe.Srno and only the latest documents.
I am stuck at the group stage.This is my code
db.AllDevices.aggregate([
{ $sort: { "Cpe.date": -1 } },
{
$group: {
_id: "$Cpe.Srno",
"latest": { $first: "$Cpe.date" }
}
},
])
and this is my result
{ "_id" : "qst3pnS3EQi8uHb", "latest" : ISODate("2016-12-04T14:51:26.487Z") }
{ "_id" : "Ur45SS1I3Yaji2p", "latest" : ISODate("2016-12-04T14:51:26.513Z") }
{ "_id" : "9ZZXVVEAQ5pA9Ax", "latest" : ISODate("2016-12-04T14:51:26.518Z") }
I need to get the whole data,all the fields(I only get two fields).I checked out $push ,$addToSet.They don't seem to work for me.
Where am I going wrong or am I missing something.
Tried $match after $group
db.AllDevices.aggregate([
{ $sort: { "Cpe.date": -1 } },
{
$group: {
_id: "$Cpe.Srno",
"latest": { $first: "$Cpe.date" }
}
},
{
$project: {
Srno: "$_id",
datetag: "$latest",
_id: 0
}
},
{ $match: { "Cpe.Srno": "$Srno", "Cpe.date": "$datetag" } }
])
Doesnt work.
You can make use of $$ROOT to push the whole document followed by project with $arrayElemAt to pick the latest.
aggregate([{
$sort: {
"Cpe.date": -1
}
}, {
$group: {
_id: "$Cpe.Srno",
"fields": {
$push: "$$ROOT"
}
}
}, {
$project: {
"latest": {
$arrayElemAt: ["$fields", 0]
}
}
}])