MongoDB double $group aggregation - mongodb

I have several documents that looks like this:
{
"_id" : ObjectId("50b59cd75bed76f46522c34e"),
"player_id" : 0,
"league_id" : 2,
"results" : [
{ "discipline" : "football",
"score" : 25.15
},
{
"discipline" : "basketball",
"score" : 21.24
},
{
"discipline" : "cycling",
"score" : 68.19
},]
}
I try to aggregate this data. First unwind results array, then leave only "football" and "cycling", next count average result. This part I did, and it is working.
My code:
db.grades.aggregate(
{$unwind:"$results"},
{$match: {$or: [{"results.discipline":"football"},{"results.discipline":"cycling"} ]}},
{$group:{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"}}},
)
Then I try to aggregate by league_id, it means, average players results in specific leagues, add to code above:
{$group:{_id:"$_id.league_id",aver_league:{$avg:$average}}}
And now code looks like:
db.grades.aggregate(
{$unwind:"$results"},
{$match: {$or: [{"results.discipline":"football"},{"results.discipline":"cycling"} ]}},
{$group:{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"}}},
{$group:{_id:"$_id.league_id",aver_league:{$avg:$average}}}
)
Console displays: JavaScript execution failed: ReferenceError: $average is not defined. What is wrong? Where did I make a mistake? Is it possible to aggregate by _id.league_id?

Try this pipeline:
[
{$unwind:"$results"},
{$match: {"results.discipline":{$in:["football", "basketball"]}}},
{$group{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"}}}
]
it works for me with your doc:
{
"result" : [
{
"_id" : {
"player_id" : 0,
"league_id" : 2
},
"average" : 23.195
}
],
"ok" : 1
}
UPD. If you want to group again, by league_id:
[{$unwind:"$results"},
{$match: {"results.discipline":{$in:["football", "basketball"]}}},
{$group:{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"} }},
{$group:{_id:"$_id.league_id", 'average':{$avg:"$average"} }} ]
{ "result" : [ { "_id" : 2, "average" : 23.195 } ], "ok" : 1 }

Related

Mongodb Query to get the nth document

I need to create a query in mongodb that needs to return the SECOND TO THE LAST document. I am planning to use $group for this query but i dont know what aggregation function to use. I only know $first and $last.
I have an example collection below and also include the expected output. Thank you!
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b4"),
"auditlogs" : [
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84be"),
"userid" : ObjectId("5ffe702d59a9205db81fcb69"),
"action" : "ADDTRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84bd"),
"userid" : ObjectId("5ffe644f9493e05db9245192"),
"action" : "EDITPROFILE"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84bc"),
"userid" : ObjectId("5ffe64949493e05db9245197"),
"action" : "DELETETRANSACTION"
} ]
"_id" : ObjectId("60dc27ac54b7c46bfa1b75ge2"),
"auditlogs" : [
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84bb"),
"userid" : ObjectId("5ffe64b69493e05db924519b"),
"action" : "ADDTRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84ba"),
"userid" : ObjectId("5ffe65419493e05db92451d4"),
"action" : "ADDTRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b9"),
"userid" : ObjectId("5ffe65689493e05db92451d9"),
"action" : "CHANGEACCESS"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b8"),
"userid" : ObjectId("5ffe65819493e05db92451dd"),
"action" : "DELETETRANSACTION"
},
{
"_id" : ObjectId("60dc27ac54b7c46bfa1b84b7"),
"userid" : ObjectId("5ffe65df9493e05db92451f3"),
"action" : "EDITPROFILE",
]
OUTPUT:
{"_id" : ObjectId("60dc27ac54b7c46bfa1b84b4"),"_id" : ObjectId("60dc27ac54b7c46bfa1b84bd"),"userid" : ObjectId("5ffe644f9493e05db9245192"),"action" : "EDITPROFILE"},
{"_id" : ObjectId("60dc27ac54b7c46bfa1b75ge2"),"_id" : ObjectId("60dc27ac54b7c46bfa1b84b8"),"userid" : ObjectId("5ffe65819493e05db92451dd"),"action" : "DELETETRANSACTION"}
You can't have two _id keys in one single object.
I've made the parent object's id to _parentId you can give it's a name anything you want except _id
Aggregation:
db.collection.aggregate([
{
$unwind: "$auditlogs"
},
{
"$project": {
"_parentId": "$_id",
"_id": "$auditlogs._id",
"action": "$auditlogs.action",
"userid": "$auditlogs.userid",
}
}
])
Playground
You can slice the array by -2 to get the last two item, then by 1 to get first one. Therefore, the array will be left the second to the last. Finally, unwind auditlogs so it can be changed from array to object which is structure that you want.
db.collection.aggregate([
{
$project: { auditlogs : { $slice: [ "$auditlogs", -2 ] } }
},
{
$project: { auditlogs : { $slice: [ "$auditlogs", 1 ] } }
},
{
$unwind: "$auditlogs"
}
])

mongodb aggregate,groupby list in object

I am looking at performing a group by upon a given value within a nest object. For example, my document structure is as follows:(Sorry about the limited data)
"_id" : "92623ba7-4ca5-46c7-8d76-c4bc8387ea00",
"Status" : 2.0,
"UploadDate" : ISODate("2018-10-30T12:01:19.619Z"),
"UpdateDate" : ISODate("2018-10-30T12:01:19.619Z"),
"Request" : "abc123",
"ShowCaseHtml" : "",
"PageResult" : [ ],
"ProductFilter" : {
"_id" : "9430fb88-2deb-4508-8422-dd67c3a35205",
"Status" : 2,
"UploadDate" : ISODate("2018-11-05T10:52:37.122Z"),
"UpdateDate" : ISODate("2018-11-05T10:52:37.122Z"),
"ProductPageType" : 0,
"Categories" : [ ],
"PriceRanges": [ ],
"Brands" : [ ],
"Sellers": [ ],
"StarRatings" : [ ],
},
"BreadCrumbs"[ ]
Result of my query returns only a part of object, what i need is full object but filterd version, here is my query
db.getCollection('dbName').aggregate(
{$match: {"Request": "abc123"}},
{$project :
{"PageResult":1,"ProductFilter":1,"BreadCrumbs":1,"ShowCaseHtml":1}},
{$unwind: "$PageResult"},
{$sort:{'PageResult.MarketPlaceProductPrice.ProductPrice':1}},
{$skip: 2},
{$limit: 3},
{$group: {"_id": "$_id", "PageResult": {$push: "$PageResult"}}},
{$project :
{"PageResult.MarketPlaceProductPrice.ProductPrice":1,"_id":0}}
)
Result of the query is like,
{
"PageResult" : [
{
"MarketPlaceProductPrice" : {
"ProductPrice" : 1519.00
}
},
{
"MarketPlaceProductPrice" : {
"ProductPrice" : 2749.00
}
},
{
"MarketPlaceProductPrice" : {
"ProductPrice" : 3359.00
}
}
]
}
i need the ProductFilter,BreadCrumbs and ShowCaseHtml areas too, that is why i specified the
{$project :
{"PageResult":1,"ProductFilter":1,"BreadCrumbs":1,"ShowCaseHtml":1}}
but i only have filtered PageResult area, any ideas ?
Using this query solved my problem
db.getCollection('dbName').aggregate(
{$match: {"Request":"abc123"}},
{$project : {"PageResult":1,"ProductFilter":1,"BreadCrumbs":1,"ShowCaseHtml":1}},
{$unwind: "$PageResult"},
{$sort:{'PageResult.MarketPlaceProductPrice.ProductPrice':1}},
{$skip: 2},
{$limit: 3},
{$group: {"_id": {"Request":"$Request","ProductFilter":"$ProductFilter","BreadCrumbs":"$BreadCrumbs","ShowCaseHtml":"$ShowCaseHtml"},"PageResult": {$push : "$PageResult"}
}}
)

How can i access N th level of Sub Document in MongoDB

{
"_id" : ObjectId("5b4d815ad85250f4e502d142"),
"company_Name" : "Rishabh Instruments",
"spaces" : [
{
"_id" : "11",
"name" : "Trishala",
"spaces" : [
{
"_id" : "111",
"name" : "ESL"
},
{
"_id" : "112",
"name" : "IS"
}
]
},
{
"_id" : "12",
"name" : "Riddhi",
"spaces" : [
{}
]
},
{
"name" : "XYZ",
"spaces" : [
{}
]
}
]
}
This is my Document structure in Mongo DB and it may have the sub documents upto N th level so for now I want to access the document named with ESL and IS. so how can i do that i am able to go upto 2nd level with below query so
db.space.aggregate([
{$match: {company_Name: 'Rishabh Instruments'}},
{$unwind: '$spaces'},
{$match: {'spaces.name': 'Trishala'}}
// {$unwind: '$spaces'},
// {$match: {'spaces.name': 'ESL'}}
])
but if i uncomment those two lines then it doesn't return any thing
so can anyone guide me or give any hint.
I tried the below solution and its working as expected i can go to N th level by chaining the key as in my case key is spaces
db.space.aggregate([
{$match: {company_Name: 'Rishabh Instruments'}},
{$unwind: '$spaces'},
{$match: {'spaces.name': 'Trishala'}},
{$unwind: '$spaces.spaces'},
{$match: {'spaces.spaces.name': 'ESL'}}
])
Try this aggregate query with projection to match the sample output you had provided :
db.space.aggregate([
{$match: {company_Name: 'Rishabh Instruments'}},
{$unwind: '$spaces'},
{$match: {'spaces.name': 'Trishala'}},
{$unwind: '$spaces.spaces'},
{$match: {$or : [{'spaces.spaces.name': 'ESL'},{'spaces.spaces.name': 'IS'}]}},
{$project : {_id : 0, _id :'$spaces.spaces._id', name:'$spaces.spaces.name'}}
])
Output :
{ "_id" : "111", "name" : "ESL" }
{ "_id" : "112", "name" : "IS" }

Get matched embedded document(s) from array

I've got a lot of documents using the following structure in MongoDB:
{
"_id" : ObjectId("..."),
"plant" : "XY_4711",
"hour" : 1473321600,
"units" : [
{
"_id" : ObjectId("..."),
"unit_id" : 10951,
"values" : [
{
"quarter" : 1473321600,
"value" : 395,
},
{
"quarter" : 1473322500,
"value" : 402,
},
{
"quarter" : 1473323400,
"value" : 406,
},
{
"quarter" : 1473324300,
"value" : 410,
}
]
}
]
}
Now I need to find all embedded document values where the quarter is between some given timestamps (eg: { $gte: 1473324300, $lte: 1473328800 }).
I've only got the unit_id and the quarter timestamp from/to for filtering the documents. And I only need the quarter and value grouped and ordered by unit.
I'm new in MongoDB and read something about find() and aggregate(). But I don't know how to do it. MongoDB 3.0 is installed on the server.
Finally I've got it:
I simply have to take apart each array, filtering out the things I don't need and put it back together:
db.collection.aggregate([
{$match : {$and : [{"units.values.quarter" : {$gte : 1473324300}}, {"units.values.quarter" : {$lte : 1473328800 }}]}},
{$unwind: "$units"},
{$unwind: "$units.values"},
{$match : {$and : [{"units.values.quarter" : {$gte : 1473324300}}, {"units.values.quarter" : {$lte : 1473328800 }}]}},
{$project: {"units": {values: {quarter: 1, "value": 1}, unit_id: 1}}},
{$group: {"_id": "$units.unit_id", "quarter_values": {$push: "$units.values"}}} ,
{$sort: {"_id": 1}}
])
Will give:
{
"_id" : 10951,
"quarter_values" : [
{
"quarter" : 1473324300,
"value" : 410
},
{
"quarter" : 1473325200,
"value" : 412
},
{
"quarter" : 1473326100,
"value" : 412
},
{
"quarter" : 1473327000,
"value" : 411
},
{
"quarter" : 1473327900,
"value" : 408
},
{
"quarter" : 1473328800,
"value" : 403
}
]
}
See: Return only matched sub-document elements within a nested array for a detailed description!
I think I have to switch to $map or $filter in the future. Thanks to notionquest for supporting my questions :)
Please see the sample query below. I didn't exactly get your grouping requirement. However, with this sample query you should be able to change and get your desired output.
db.collection.aggregate([
{$unwind : {path : "$units"}},
{$match : {$and : [{"units.values.quarter" : {$gte : 1473324300}}, {"units.values.quarter" : {$lte : 1473328800 }}]}},
{$project : {"units" : {values : {quarter : 1, "value" : 1}, unit_id : 1}}},
{$group : { _id : "$units.unit_id", quarter_values : { $push :{ quarter : "$units.values.quarter", value : "$units.values.value"}}}},
{$sort : {_id : 1 }}
]);
Sample output:-
{
"_id" : 10951,
"quarter_values" : [
{
"quarter" : [
1473321600,
1473322500,
1473323400,
1473324300
],
"value" : [
395,
402,
406,
410
]
}
]
}

Complex query of MongoDB

I have looked up the manual of MongoDB, but cannot find a proper solution to do the following search:
Document structure:
{
"_id" : ObjectId("57201082e92c07baef62452a"),
"user_id" : 1,
"profile" : [
{
"date" : ISODate("2012-10-11T12:58:06.000Z"),
"content" : [
{
"feature_id" : 1,
"feature_value" : true
}
]
},
{
"date" : ISODate("2013-04-12T8:23:09.000Z"),
"content" : [
{
"feature_id" : 1,
"feature_value" : false
}
]
}
]
}
What I want to get is the LATEST feature_value of a given user_id and a given feature_id.
Is there any way to accomplish this using MongoDB?
Thanks.
Assuming the latest(as profile.date) you can use the following to get the desired result.(query not tested, hope it works).
db.test.aggregate(
[
{$match : {"user_id" : 1}},
{$unwind : "$profile"},
{$unwind : "$profile.content"},
{$match : {"profile.content.feature_id" : 1}},
{$sort : {"profile.date" : -1}},
{$limit : 1},
{$group:{_id : "$_id", content : {$push:"$profile.content"}}}
])
Please try this:
db.test.aggregate(
{$match:{"user_id" :1,"profile.content.feature_id":1}},
{$project:{"profile.content.feature_value":1}},
{$sort:{"profile.date" :-1}},
{$limit : 1});