I am fetching for category and subcategory i am using the following pipeline
Category:
{
"$match": {
"category": {
"$in": ["list of my categories"]
}
}
},
{
"$group": {
"category": "$category",
"count": {
"$sum": 1
}
}
},
This gives me:
{category: category name,
count: totalcount}
Subcategory pipeline
{
"$match": {
"category": {
"$in": ["list of my categories"]
}
}
},
{
"$group": {
"_id": { subCategory: "$subCategory", category: "$category" },
"count": {
"$sum": 1
}
}
},
{
"$group": {
"_id": "$_id.category",
"counts": {
"$push": {
"k": "$_id.subCategory",
"v": "$count"
},
},
"count":{
$sum: "$counts"
}
}
},
{
"$project": {
"counts": { "$arrayToObject": "$counts" },
}
},
This gives me
category: name {
subcategory1 : total count,
...
}
How do I join the two to get a single call to return something like
{category: categoryname,
count: totalcountforcategory,
subcategories: {
subcategory: totalcount,
subcategory2:totalcount}
Update
Here is my sample JSON
{
"category": "Category one",
"name": "Sample name",
"subCategory": "subCategory one",
},
{
"category": "Category one",
"name": "Sample name",
"subCategory": "subCategory two",
},
{
"category": "Category two",
"name": "Sample name",
"subCategory": "subCategory one",
},
{
"category": "Category one",
"name": "Sample name",
"subCategory": "subCategory two",
}
Expected OutPut
{
"Category one": 3,
subCategories: {
"subCategoryone": 2,
"subCategorytwo":3,
}
}
{
"Category two": 5,
subCategories: {
"subCategoryone": 2,
"subCategorytwo":3,
}
}
db.collection.aggregate([
{
"$group": {
"_id": {
cat: "$category",
sub: "$subCategory"
},
"count": {
"$sum": 1
},
"subCategory": {
$push: "$$ROOT"
}
}
},
{
"$group": {
"_id": "$_id.cat",
"counts": {
"$push": {
"k": "$_id.sub",
"v": "$count"
},
},
"count": {
$sum: "$counts"
}
}
},
{
"$project": {
"counts": {
"$arrayToObject": "$counts"
},
}
},
])
Playground
I used $facet to join pipelines i don't know if it is best practice or not
const pipeline = [
{
"$facet": {
"GroupAll":
[{ $project: { name: 1, category: 1,subCategory:1,votes:1 } }]
,
"GroupTotal": [
{
"$match": {
"nominationYear": {
"$eq": "2022"
}
}
},
{
$count: "total"
}
],
"GroupCategories": [
{
"$match": {
"category": {
"$in": ["Categories"]
}
}
},
{
"$group": {
"_id": { category: "$category" },
"count": {
"$sum": 1
}
}
},
],
"GroupSubCategories": [
{
"$match": {
"category": {
"$in": ["Categories"]
}
}
},
{
"$group": {
"_id": { subCategory: "$subCategory", category: "$category" },
"count": {
"$sum": 1
}
}
},
{
"$group": {
"_id": "$_id.category",
"counts": {
"$push": {
"k": "$_id.subCategory",
"v": "$count"
},
},
"count": {
$sum: "$counts"
}
}
},
{
"$project": {
"counts": { "$arrayToObject": "$counts" },
}
},
]
}
}
]
And here is my output
{
"nominations": [
{
"GroupAll": [""],
"GroupTotal": [""],
"GroupCategories": [""],
"GroupSubCategories":[""]
}
],
"total": ""
}
I achieved it using $facet to join multiple pipelines
Related
I have this collection:
[{
"_id": "5c378eecd11e570240a9b0ac",
"userID": "1",
"isActive": "Active",
"areaCode": "A-1",
"__v": 0
},
{
"_id": "5c378eecd11e570240a9b0bb",
"userID": "1",
"isActive": "Active",
"areaCode": "A-2",
"__v": 0
},
{
"_id": "5c378eecd11e570240a9b0c5",
"userID": "2",
"isActive": "Active",
"areaCode": "A-1",
"__v": 0
}]
Need help in grouping the results by user ID then the area code using aggregation but I'm not getting the desired output. Here's what I've tried:
AreaCodes.aggregate([
{
'$match': { '$and': [
{ 'isActive': 'Active' },
{ 'userID': { '$exists': true } }
]
}
},
{
'$group': {
'_id': {
'userID': '$userID'
},
'entries': {
'$push': {
'areaCode': '$areaCode'
}
}
}
},
{
'$group': {
'_id': '$_id.userID',
'areaCodes': {
'$push': {
'areaCode': '$entries'
}
}
}
},
{
'$project': {
'_id': 0,
'userID': '$_id',
'areaCodes': '$areaCodes'
}
}
])
Which returns the following:
[
{
"userID": "1",
"areaCodes": [
{
"areaCode": [
{
"areaCode": "A-1"
},
{
"areaCode": "A-2"
}
]
}
]
},
{
"userID": "2",
"areaCodes": [
{
"areaCode": [
{
"areaCode": "A-1"
}
]
}
]
}
]
My desired output would be to remove the excess areaCode objects and group them inside an array for each user like:
[
{
"userID": "1",
"areaCodes": ["A-1", "A-2"]
},
{
"userID": "2",
"areaCodes": ["A-1"]
}
]
How to achieve this format? Thanks.
How about:
db.collection.aggregate([
{
$match: {
$and: [{ "isActive": "Active" }, {"userID": {"$exists": true}}]
}
},
{
$group: {
_id: '$userID',
areaCodes: {$addToSet: "$areaCode"}
}
},
{
$project: {
_id: 0,
userID: "$_id",
areaCodes: 1
}
}
])
As you can see on this playgeound example.
If you just want the matching areaCodes, you can simply use $addToSet.
i have models look like,
openPositionsDetail is array of object
const accountSummary = new Schema({
account: {
account: SchemaType.String,
pialangCode: SchemaType.String,
BranchOffice: SchemaType.String,
},
openPositionsDetail: [],
qry_obj: {},
idStatement: Number,
createdBy: String
});
i want to get this document data if at least in openPositionsDetail.net not 0 (not 0, include negative number or positive number)
this is my document look like
{
"_id": ObjectId("5f2a35baec826fa4a9eaa186"),
"openPositionsDetail": [
{
"buy": NumberInt("332"),
"sell": NumberInt("310"),
"locking": NumberInt("350"),
"net": NumberInt("300"),
"product": NumberInt("5"),
"productName": "XUL10"
},
{
"buy": NumberInt("5"),
"sell": NumberInt("3"),
"locking": NumberInt("350"),
"net": NumberInt("0"),
"product": NumberInt("5"),
"productName": "HKK"
}
],
"account": {
"account": "RZBJ2889",
"pialangCode": "EWF",
"BranchOffice": "SSC"
},
"idStatement": NumberInt("25009"),
"createdBy": "RIZAL",
"qry_obj": {
"ntm": NumberInt("493500"),
"dtm": NumberInt("283500")
},
"__v": NumberInt("0")
},
//2
{
"_id": ObjectId("5f2a35a1ec826fa4a9ea41bb"),
"openPositionsDetail": [
{
"buy": NumberInt("635"),
"sell": NumberInt("1"),
"locking": NumberInt("1"),
"net": NumberInt("634"),
"product": NumberInt("5"),
"productName": "XUL10"
}
],
"account": {
"account": "RDZ21797",
"pialangCode": "RFB",
"BranchOffice": "DBS"
},
"idStatement": NumberInt("486"),
"createdBy": "RIZAL",
"qry_obj": {
"ntm": NumberInt("887810"),
"dtm": NumberInt("444010")
},
"__v": NumberInt("0")
}
// 3
{
"_id": ObjectId("5f2a35b6ec826fa4a9ea92ca"),
"openPositionsDetail": [
{
"buy": NumberInt("1"),
"sell": NumberInt("1"),
"locking": NumberInt("5"),
"net": NumberInt("0"),
"product": NumberInt("5"),
"productName": "XUL10"
},
{
"buy": NumberInt("3"),
"sell": NumberInt("3"),
"locking": NumberInt("0"),
"net": NumberInt("0"),
"product": NumberInt("6"),
"productName": "JPK"
}
],
"account": {
"account": "RDW22175",
"pialangCode": "RFB",
"BranchOffice": "DBS"
},
"idStatement": NumberInt("21237"),
"createdBy": "RIZAL",
"qry_obj": {
"ntm": NumberInt("572250"),
"dtm": NumberInt("286650")
},
"__v": NumberInt("0")
}
this my query right now, but i am not sure this will work in array data
AccountSummary.aggregate([
{ $sort: { 'account.account': 1} },
{
$match: {
$and: filter.concat({
$expr: { $not: {"openPositionsDetail.net": 0} }
})
},
},
{
$facet: {
"stage1": [{ "$group": { _id: null, count: { $sum: 1 } } }],
"stage2": [{ "$skip": data.offset }, { "$limit": data.limit }]
}
},
{ $unwind: "$stage1" },
{
$project: {
count: "$stage1.count",
data: "$stage2"
}
}
])
im newcomer in mongodb
You can do as below
Unwind
Then check not 0 condition
Group back.
Update:
Working sample -
play
db.collection.aggregate([
{
"$unwind": "$openPositionsDetail"
},
{
"$project": {
"notZero": {
"$ne": [
"$openPositionsDetail.net",
0
]
},
"openPositionsDetail": 1
}
},
{
"$match": {
notZero: true
}
},
{
"$group": {
"_id": "$_id",
data: {
$push: "$$ROOT"
}
}
}
])
I have the following structure in my collection (you don't have to mind the status) :
{
"_id": {
"$oid": "5e6355e71b14ee00175698cb"
},
"finance": {
"expenditure": [
{
"status": true,
"_id": { "$oid": "5e63562d1b14ee00175698df" },
"amount": { "$numberInt": "100" },
"category": "Sport"
},
{
"status": true,
"_id": { "$oid": "5e6356491b14ee00175698e0" },
"amount": { "$numberInt": "200" },
"category": "Sport"
},
{
"status": true,
"_id": { "$oid": "5e63565b1b14ee00175698e1" },
"amount": { "$numberInt": "50" },
"category": "Outdoor"
},
{
"status": true,
"_id": { "$oid": "5e63566d1b14ee00175698e2" },
"amount": { "$numberInt": "400" },
"category": "Outdoor"
}
]
}
}
My previos command was this:
User.aggregate([
{ $match: {_id: req.user._id} },
{ $unwind: '$finance.expenditure' },
{ $match: {'finance.expenditure.status': true} },
{ $sort: {'finance.expenditure.currentdate': -1} },
{
$group: {
_id: '$_id',
expenditure: { $push: '$finance.expenditure' }
}
}
])
With this I just get every single expenditure back.
But now I want to group the expenditures by their category and sum up the amount of every single expenditure for their group.
So it should look like this:
{ "amount": 300 }, "category": "Sport" },
{ "amount": 450 }, "category": "Outdoor" }
Thanks for your help
Instead of grouping on _id field group on category field & sum amount field:
db.collection.aggregate([
{ $match: {_id: req.user._id}},
{
$unwind: "$finance.expenditure"
},
{
$match: {
"finance.expenditure.status": true
}
},
{
$sort: {
"finance.expenditure.currentdate": -1
}
},
{
$group: {
_id: "$finance.expenditure.category",
amount: {
$sum: "$finance.expenditure.amount"
}
}
},
{
$project: {
_id: 0,
category: "$_id",
amount: 1
}
}
])
Test : MongoDB-Playground
I have a collection similar to this:
[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
I want to aggregate into something like this:
{
"breakfast":[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
}
],
"lunch":[
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
}
I have only managed to group them but I can't change the key meals to either breakfast or lunch depending on the meal.name(group name)
$group: {
_id: { meal: '$meal.name' },
meals: { $push: '$meal' },
}
Using the above code I have managed to produce the output below. My only challenge is changing the key meals to either breakfast or lunch as explained above in the subgroups.
{
"meals":[
{
"_id":1,
"name":"breakfast",
"time":"10.00"
},
{
"_id":2,
"name":"breakfast",
"time":"10.10"
}
],
"meals":[
{
"_id":3,
"name":"lunch",
"time":"12.07"
},
{
"_id":4,
"name":"lunch",
"time":"12.45"
}
]
}
Here you can have your answer .
After "grouping" to add to an array you similarly $push all that content into array by the "name" grouping key and then convert into keys of a document in a $replaceRoot with $arrayToObject:
db.collection.aggregate([
{ "$group": {
"_id": "$name",
"data": { "$push": "$$ROOT" }
}},
{ "$group": {
"_id": null,
"data": {
"$push": {
"k": "$_id",
"v": "$data"
}
}
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$data" }
}}
])
OUTPUT
[
{
"breakfast": [
{
"_id": 1,
"name": "breakfast",
"time": "10.00"
},
{
"_id": 2,
"name": "breakfast",
"time": "10.10"
}
],
"lunch": [
{
"_id": 3,
"name": "lunch",
"time": "12.07"
},
{
"_id": 4,
"name": "lunch",
"time": "12.45"
}
]
}
]
You can check the result of above query in this LINK
I'm trying to modify the second pipeline from this query (which I got from here nodejs + mongoose - query aggregate
db.todos.aggregate([
{
"$group": {
"_id": "$pic",
"open_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "open" ] }, 1, 0 ]
}
},
"progress_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "progress" ] }, 1, 0 ]
}
},
"done_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "done" ] }, 1, 0 ]
}
},
"archive_count": {
"$sum": {
"$cond": [ { "$eq": [ "$status", "archive" ] }, 1, 0 ]
}
}
}
},
{
"$group": {
"_id": "$_id",
"detail": {
"$push": {
"name": "open",
"$todos": "$open_count"
},
"$push": {
"name": "progress",
"$todos": "$progress_count"
},
"$push": {
"name": "done",
"$todos": "$done_count"
},
"$push": {
"name": "archive",
"$todos": "$archive_count"
}
}
}
},
{
"$project": {
"_id": 0, "pic": "$_id", "detail": 1
}
}
])
I want this kind of JSON structure so I can put it on google chart, which the format is like this:
[
{
"pic": "A",
"detail": [
{
"name": "open",
"todos": 2
},
{
"name": "progress",
"todos": 1
},
{
"name": "done",
"todos": 8
},
{
"name": "archive",
"todos": 20
}
],
"pic": "B",
"detail": [
{
"name": "open",
"todos": 5
},
{
"name": "progress",
"todos": 2
},
{
"name": "done",
"todos": 5
},
{
"name": "archive",
"todos": 10
}
],
}
]
But I got this error
exception: FieldPath 'progress' doesn't start with $
Try with this aggregation query:
db.todos.aggregate([
{
"$group": {
"_id": {
"pic": "$pic",
"name": "$status"
},
"todos": {
"$sum": 1
}
}
},
{
"$project": {
"_id": 0,
"pic": "$_id.pic",
"detail": {
"name": "$_id.name",
"todos": "$todos"
}
}
},
{
"$group": {
"_id": "$pic",
"detail": {
"$push": "$detail"
}
}
},
{
"$project": {
"_id": 0, "pic": "$_id", "detail": 1
}
}])