How can i access N th level of Sub Document in MongoDB - 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" }

Related

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
]
}
]
}

mongodb: Unable to fetch results from $project and $eq

below code first joins two collections on one field and tries to filter values on other field.
db.zeroDimFacts.aggregate(
{$lookup:{from:"zeroDim",localField:"Kind",foreignField:"Model", as:"EmbedUp"}},
{$project: {"EmSub":"$EmbedUp.Sub","Result": {$eq:["$Type","$EmbedUp.Sub"]}, "type":"$Type"}})
Please check the below output of the code. Even though 'EmSub' & '$Type' are having equal values, it does not show up in 'Result' field.
If it is because 'EmSub' is displayed as array, how do I compare only value containing in that array?
/* 1 */
{
"_id" : NumberLong(1),
"EmSub" : [
"Fruit"
],
"Result" : false,
"type" : "Fruit"
}
/* 2 */
{
"_id" : NumberLong(2),
"EmSub" : [
"Fruit"
],
"Result" : false,
"type" : "Fruit"
}
/* 3 */
{
"_id" : NumberLong(3),
"EmSub" : [
"Fruit"
],
"Result" : false,
"type" : "Fruit"
}
You can unwind the array to compare with its elements like this:
db.zeroDimFacts.aggregate([{$lookup:{from:"zeroDim",localField:"Kind",foreignField:"Model", as:"EmbedUp"}},
{$unwind: "$EmbedUp"},
{$project: {"EmSub":"$EmbedUp.Sub","Result": {$eq:["$Type","$EmbedUp.Sub"]}, "type":"$Type"}}]);
It can give you more than one result for a single zeroDimFacts _id, however you can group them again
db.zeroDimFacts.aggregate([{$lookup:{from:"zeroDim",localField:"Kind",foreignField:"Model", as:"EmbedUp"}},
{$unwind: "$EmbedUp"},
{$project: {"EmSub":"$EmbedUp.Sub","Result": {$eq:["$Type","$EmbedUp.Sub"]}, "type":"$Type"}},
{$group: {_id: "$_id", EmSub: {$push: "$EmSub"}, Result: {$push: "$Result"}, type: {$push: "$type"}}}]);

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

MongoDB double $group aggregation

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 }

MongoDB: Query by size of array with a filtered value

In MongoDB, I have a collection ("users") with the following basic schema:
{
"_id" : ObjectId("50e5de00b623143995c5b739")
"name" : "Jon",
"emails_sent" : [
{
"type" : "invite",
"sent_time" : ISODate("2013-04-21T21:11:50.999Z")
},
{
"type" : "invite",
"sent_time" : ISODate("2013-04-15T21:10:35.999Z")
},
{
"type" : "follow",
"sent_time" : ISODate("2013-04-21T21:11:50.999Z")
}
]
}
I'd like to query for users based on the $size of emails_sent of a certain "type" only, e.g. only count "invite" emails. Is there any way to achieve this sort of "filtered count" in a standard mongo query?
Many thanks.
db.users.aggregate([
{$unwind:'$emails_sent'},
{$match: {'$emails_sent.type':'invite'}},
{$group : {_id : '$name', sumOfEmailsSent:{$sum:1} }}
]);
BTW you are missing a square bracket which closes the $emails_sent array.