Mongodb aggregate grouping elements of an array type field - mongodb

I have below data in my collection:
[
{
"_id":{
"month":"Jan",
"year":"2022"
},
"products":[
{
"product":"ProdA",
"status":"failed",
"count":15
},
{
"product":"ProdA",
"status":"success",
"count":5
},
{
"product":"ProdB",
"status":"failed",
"count":20
},
{
"product":"ProdB",
"status":"success",
"count":10
}
]
},
...//more such data
]
I want to group the elements of products array on the name of the product, so that we have record of how what was the count of failure of success of each product in each month. Every record is guaranteed to have both success and failure count each month. The output should look like below:
[
{
"_id":{
"month":"Jan",
"year":"2022"
},
"products":[
{
"product":"ProdA","status":[{"name":"success","count":5},{"name":"failed","count":15}]
},
{
"product":"ProdB","status":[{"name":"success","count":10},{"name":"failed","count":20}]
}
]
},
...//data for succeeding months
]
I have tried to do something like this:
db.collection.aggregate([{ $unwind: "$products" },
{
$group: {
"_id": {
month: "$_id.month",
year: "$_id.year"
},
products: { $push: { "product": "$product", status: { $push: { name: "$status", count: "$count" } } } }
}
}]);
But above query doesn't work.
On which level I need to group fields so as to obtain above output.
Please help me to find out what I am doing wrong.
Thank You!

Your first group stage needs to group by both the _id and the product name, aggregate a list of status counts and then another group stage which then forms the products list:
db.collection.aggregate([
{$unwind: "$products"},
{$group: {
_id: {
id: "$_id",
product: "$products.product",
},
status: {
$push: {
name: "$products.status",
count: "$products.count"
}
}
}
},
{$group: {
_id: "$_id.id",
products: {
$push: {
product: "$_id.product",
status: "$status"
}
}
}
}
])
Mongo Playground

Related

Mongodb aggregate group by array elements

I have a mongodb document that contains customer id, status (active, deactivate) and date.
[
{
id:1,
date:ISODate('2022-12-01'),
status:'activate'
},
{
id:2,
date:ISODate('2022-12-01'),
status:'activate'
},
{
id:1,
date:ISODate('2022-12-02'),
status:'deactivate'
},
{
id:2,
date:ISODate('2022-12-21'),
status:'deactivate'
}
]
I need to get daywise customer status count.
I came up with below aggregation.
db.collection.aggregate([
{
$addFields: {
"day": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$date"
}
}
}
},
{
$group: {
_id: "$day",
type: {
$push: "$status"
}
}
}
])
this way I can get status in a array. like below.
[
{
_id:"2022-12-01",
type:[
0:"activate",
1:"activate"
]
},
{
_id:"2022-12-02",
type:[
0:"deactivate"
]
},
{
_id:"2022-12-21",
type:[
0:"deactivate"
]
}
]
now it's working as intended. but I need the output like below.
[
{
_id:"2022-12-01",
type:{
"activate":2,
}
},
{
_id:"2022-12-02",
type:{
"deactivate":1
}
},
{
_id:"2022-12-21",
type:{
"deactivate":1
}
}
]
this table has around 100,000 documents and doing this programmatically will take about 10 seconds. that's why I'm searching a way to do this as a aggregation
One option is to group twice and then use $arrayToObject:
db.collection.aggregate([
{$group: {
_id: {day: "$date", status: "$status"},
count: {$sum: 1}
}},
{$group: {
_id: {$dateToString: {format: "%Y-%m-%d", date: "$_id.day"}},
data: {$push: {k: "$_id.status", v: "$count"}}
}},
{$project: {type: {$arrayToObject: "$data"}}}
])
See how it works on the playground example

How to group mongodb aggregate array of objects data To sum numbers on the same date

I'm trying to create charts but I can't combine the data to be like "Expected result below"
What can I use to return "Expected result".
I tried using $group in $group and $reduce and it didn't work well.
I hope someone can help me solve this task
Current result is
[
{
"_id":"5fd4c3586e83b334d97c5218",
"consumption":5,
"charts":[
{
"date":"2020-10",
"consumption":1
},
{
"date":"2020-10",
"consumption":1
},
{
"date":"2020-11",
"consumption":1
},
{
"date":"2020-11",
"consumption":1
}
]
}
]
Expected result is
[
{
"_id":"5fd4c3586e83b334d97c5218",
"consumption":5,
"charts":[
{
"date":"2020-10",
"consumption":2
},
{
"date":"2020-11",
"consumption":2
},
}
]
You need to go with aggregations
db.collection.aggregate([
{ $unwind: "$charts" },
{
$group: {
_id: { _id: "$_id", month: "$charts.date.month", year: "$charts.date.year" },
consumption: { $first: "$consumption" },
total: { $sum: "$charts.consumption" },
date: { $first: "$charts.date" }
}
},
{
$group: {
_id: "$_id._id",
consumption: { $first: "$consumption" },
charts: {
$push: {
date: "$date",
consumption: "$total"
}
}
}
}
])
Working Mongo playground

Mongodb aggregation , group by items for the last 5 days

I'm trying to get the result in some form using mongodb aggregation.
here is my sample document in the collection:
[{
"_id": "34243243243",
"workType": "TESTWORK1",
"assignedDate":ISODate("2021-02-22T00:00:00Z"),
"status":"Completed",
},
{
"_id": "34243243244",
"workType": "TESTWORK2",
"assignedDate":ISODate("2021-02-21T00:00:00Z"),
"status":"Completed",
},
{
"_id": "34243243245",
"workType": "TESTWORK3",
"assignedDate":ISODate("2021-02-20T00:00:00Z"),
"status":"InProgress",
}...]
I need to group last 5 days data in an array by workType count having staus completed.
Expected result:
{_id: "TESTWORK1" , value: [1,0,4,2,3] ,
_id: "TESTWORK2" , value: [3,9,,3,5],
_id : "TESTWORK3", value: [,,,3,5]}
Here is what I'm trying to do, but not sure how to get the expected result.
db.testcollection.aggregate([
{$match:{"status":"Completed"}},
{$project: {_id:0,
assignedSince:{$divide:[{$subtract:[new Date(),$assignedDate]},86400000]},
workType:1
}
},
{$match:{"assignedSince":{"lte":5}}},
{$group : { _id:"workType", test :{$push:{day:"$assignedSince"}}}}
])
result: {_id:"TESTWORK1": test:[{5},{3}]} - here I'm getting the day , but I need the count of the workTypes on that day.
Is there any easy way to do this? Any help would be really appreciated.
Try this:
db.testcollection.aggregate([
{
$match: { "status": "Completed" }
},
{
$project: {
_id: 0,
assignedDate: 1,
assignedSince: {
$toInt: {
$divide: [{ $subtract: [new Date(), "$assignedDate"] }, 86400000]
}
},
workType: 1
}
},
{
$match: { "assignedSince": { "$lte": 5 } }
},
{
$group: {
_id: {
workType: "$workType",
assignedDate: "$assignedDate"
},
count: { $sum: 1 }
}
},
{
$group: {
_id: "$_id.workType",
values: { $push: "$count" }
}
}
]);

MongoDB query based on last query by max date

I have the following collection
{
_id: ObjectId(...),
consumerId: "...",
dealId: "...",
status: "...",
stage: 0,
createdDate: 2020-09-06T00:17:16.719+00:00
}
I would like to figure out how to get distinct documents given a specific consumerId and status not equal to say "x" or "y" based on the latest createdDate. Also what would be the best indexes of fields for the collection?
You can use $match to apply the condition. $sort helps to do descending order of createdDate. Then groups helps to give the distinct data with the help of $first
[
{
$match: {
$expr: {
$and: [
{$eq: ["$consumerId","1"]},
{
$not: {
$in: ["$status",["x","y"]]
}
}
]
}
}
},
{
$sort: {
createdDate: -1
}
},
{
$group: {
_id: "$consumerId",
consumerId: { $first: "$consumerId" },
dealId: { $first: "$dealId" },
status: { $first: "$status" },
stage: { $first: "$stage" },
createdDate: { $first: "$createdDate" }
}
}
]
Working Mongo playground

total of all groups totals using mongodb

i did this Aggregate pipeline , and i want add a field contains the Global Total of all groups total.
{ "$match": query },
{ "$sort": cursor.sort },
{ "$group": {
_id: { key:"$paymentFromId"},
items: {
$push: {
_id:"$_id",
value:"$value",
transaction:"$transaction",
paymentMethod:"$paymentMethod",
createdAt:"$createdAt",
...
}
},
count:{$sum:1},
total:{$sum:"$value"}
}}
{
//i want to get
...project groups , goupsTotal , groupsCount
}
,{
"$skip":cursor.skip
},{
"$limit":cursor.limit
},
])
you need to use $facet (avaialble from MongoDB 3.4) to apply multiple pipelines on the same set of docs
first pipeline: skip and limit docs
second pipeline: calculate total of all groups
{ "$match": query },
{ "$sort": cursor.sort },
{ "$group": {
_id: { key:"$paymentFromId"},
items: {
$push: "$$CURRENT"
},
count:{$sum:1},
total:{$sum:"$value"}
}
},
{
$facet: {
docs: [
{ $skip:cursor.skip },
{ $limit:cursor.limit }
],
overall: [
{$group: {
_id: null,
groupsTotal: {$sum: '$total'},
groupsCount:{ $sum: '$count'}
}
}
]
}
the final output will be
{
docs: [ .... ], // array of {_id, items, count, total}
overall: { } // object with properties groupsTotal, groupsCount
}
PS: I've replaced the items in the third pipe stage with $$CURRENT which adds the whole document for the sake of simplicity, if you need custom properties then specify them.
i did it in this way , project the $group result in new field doc and $sum the sub totals.
{
$project: {
"doc": {
"_id": "$_id",
"total": "$total",
"items":"$items",
"count":"$count"
}
}
},{
$group: {
"_id": null,
"globalTotal": {
$sum: "$doc.total"
},
"result": {
$push: "$doc"
}
}
},
{
$project: {
"result": 1,
//paging "result": {$slice: [ "$result", cursor.skip,cursor.limit ] },
"_id": 0,
"globalTotal": 1
}
}
the output
[
{
globalTotal: 121500,
result: [ [group1], [group2], [group3], ... ]
}
]