MongoDB- Query Embedded Document, but Projection Showing Path to key/value? - mongodb

So basically I want
db.scoreFacts.find(
{"instrumentRanges.flute.minPitch": {$gte: 0, $lte:56}},
{"instrumentRanges.flute.minPitch": 1})
to return
{ "_id" : "Bach_Brandenburg5_Mov1.xml", "minPitch" : 50 }
but instead I get:
{ "_id" : "Bach_Brandenburg5_Mov1.xml", "instrumentRanges" : { "flute" : { "minPitch" : 50 } } }
Essentially the path to "minPitch" is returned, which is not what I need. How can I achieve my desired output with only .find() (no map, etc)? Thanks.

You can't do this with a standard .find() query. If you wish to alter the document structure, look into using an aggregate() call. You can then use projection to define the resulting field(s) you desire.
For example:
db.scoreFacts.aggregate([
{ $match: {"instrumentRanges.flute.minPitch": {$gte: 0, $lte:56}} },
{ $project: {"minPitch": "$instrumentRanges.flute.minPitch"} }
]);
For more information, please see the relevant documentation. Additionally, take a look at the prerequisite aggregation pipeline section.
Note: I have not tested the above query myself, so you may need to alter it somewhat to get the behavior you want.

Related

How to fetch value from inner object in mongo query result

I'm new to mongodb.
I have this object in my collection:
{
"_id" : ObjectId("5b549be38d9f1c00160117d3"),
"name" : "Name of object",
"a" : {
"b" : {
"c" : 100
}
}
}
What interests me is the 100 value, I want to fetch it from the object.
When I query the collection like this:
db.getCollection('myCollection').find({}, {'name':1, 'a.b.c':1})
I only get the same object with the inner objects.
Is there a way to query it so that I will get a result like this:
{"Name": "Name of object", "c":100}
By using Mongo aggregate query you can get the result. In $project stage of Mongo aggregate query you can add the conditions as per requirement.
Please try this query, might you will get the result:
db.myCollection.aggregate({
$project: {
"name": "$name",
"c": "$a.b.c",
_id: 0
}
})
As suggested by #Mayuri, You can achieve this by using .aggregate() but if you wanted to look at difference between $project in aggregation (Vs) projection in .find(), check out :
So one first thing with $project is it is much more powerful & can accept a lot more features that are helpful to transform the fields. But projection in .find() is pretty straight forward that can accept only few things : projection i.e; value to a field in projection can be one of these :
1 or true to include the field in the return documents.
0 or false to exclude the field.
Projection Operators : $, $elemMatch, $slice, $meta
Actual Issue :
In your query when you're doing 'a.b.c':1 in projection it means you're returning c field in the output, As field c is nested in b & a the output structure doesn't change & you'll be getting the c value under the same structure, but if you use aggregation { $project: { "c": "$a.b.c" } } it means you're assigning value of "$a.b.c" to a field named c. How ? : So when $ is used against a field in aggregation it will access field's value, which helps for assignment.

How can I traverse a nested document in MongoDB and return the sub-tree?

I have a document with the following structure in MongoDB:
{
"k1": {
"k11": {<extended-sub-document-11>},
"k12": {<extended-sub-document-12>}
},
"k2": {
"k21": {<extended-sub-document-21>}
}
}
How can I fetch the entire object at k12? The find mechanism requires me to provide a value against which to match. But here, I simply want to traverse the path k1/k12 and retrieve the entire sub-document at that key.
Thanks in advance.
Using projection in .find() you can try as like below :
db.collection.find({}, { "k1.k12": 1})
Test : mongoplayground
Note : You would only get values/object of k12 but as it's nested in k1, In the output you would see same structure with just k12 object in k1 like : {k1: {k12: {...}}}.
Using aggregation's $project stage :
db.collection.aggregate([ { $project: {_id :0, k12: "$k1.k12" } } ])
Test : mongoplayground
By using aggregation $project which is way more powerful than projection in .find() you can assign a field's value to a field. In the above query we're assigning value at k1.k12 to a field k12 using $ (Which helps to get value of referenced field).

runCommand vs aggregate method to do aggregation

To run aggregation query it is possible to use either of these:
db.collectionName.aggregate(query1);
OR
db.runCommand(query2)
But I noticed something bizarre this morning. While this:
db.runCommand(
{
"aggregate":"collectionName",
allowDiskUse: true,
"pipeline":[
{
"$match":{
"field":param
}
}
]
});
fails with error:
{
"ok" : 0.0,
"errmsg" : "aggregation result exceeds maximum document size (16MB)",
"code" : 16389,
"codeName" : "Location16389"
}
This:
db.collectionName.aggregate([
{
$match: {
field: param
}
}
])
is working (gives the expected aggregation result).
How is this possible?
Well the difference is of course that the .aggregate() method returns a "cursor", where as the options you are providing to runCommand() you are not. This actually was the legacy form which returned the response as a single BSON document with all it's limitations. Cursors on the other hand do not have the limitation.
Of course you can use the runCommand() method to "make your own cursor" with the shell, since after-all that is exactly what the .aggregate() method is doing "under the covers". The same goes for all drivers, which essentially invoke the database command for everything.
With the shell, you can transform your request like this:
var cmdRes = db.runReadCommand({
"aggregate": "collectionName",
"allowDiskUse": true,
"pipeline":[
{
"$match":{
"field":param
}
}
],
"cursor": { "batchSize": 25 }
});
var cursor = new DBCommandCursor(db, cmdRes);
cursor.next(); // will actually iterate the cursor
If you really want to dig into it then type in db.collectionName.aggregate without the parenthesis () so you actually print the function definition. This will show you some other function calls and you can dig further into them and eventually see what is effectively the lines shown above, amongst a lot of other stuff.
But the way you ran it, it's a "single BSON Document" response. Run it the way shown here, and you get the same "cursor" response.

MongoDB: how to define the response object using $match and $project

I'm pretty new to MongoDB and I'm trying to query the database and get an object like this in response.
{
activity: 'Aerial Yoga',
participants_12: 10,
participants_13_15: 10,
participants_16_18: 10,
participants_19: 10,
}
I will place an image instead of the full json so I can explain how to get the data:
I can filter but I still don't know how to format the object the DB returns to me. I'm using this query so far:
db.applications.aggregate([
{ $match : {application_number:'SL-O2-2017-05-15-000230', 'sport_activity_list.value.0.properties.sport_activity.value':'Aerial Yoga'}},
{ $project : { _id: 0, "sport_activity_list.value":1,} }
])
How can I achieve the result I want? Should I be using $project? I'm using v3.2.8 of the mongodb
Yes, $aggregate will do this for you. In the $project step you can specify that existing fields should be included or not included with truthy values, but you can also included computed fields, of which this is a very simple example:
db.applications.aggregate([
{ $match : {application_number:'SL-O2-2017-05-15-000230', 'sport_activity_list.value.0.properties.sport_activity.value':'Aerial Yoga'}},
{ $project : {
_id: 0,
value: '$sport_activity_list.value.0.properties.sport_activity.value',
participants_12: '$sport_activity_list.value.0.properties.participants_12.value',
...
} }
])
Hope that helps.

MongoDB querying by sub-document value not working as expected

I have two schemas, a Profile and a LevelOfNeed.
Profile
{
"_id" : ObjectId("56d35960a695dfa140137fca"),
. . .
"levelOfNeedServiced" : ObjectId("56d35828a695dfa140137fc7")
}
Level of Need
{
"_id" : ObjectId("56d35828a695dfa140137fc7"),
"sortOrder" : 2,
"description" : "Moderate Needs",
"additionalCost" : 3,
"__v" : 0
}
I currently have 4 documents for LevelOfNeed. What I need to do is select all of the Profile documents where the levelOfNeedServiced.sortOrder is >= a value.
Example:
db.getCollection('profiles').find({
'levelOfNeedServiced.sortOrder': { $gte: 2 }
})
Given my data, I would expect to see the example Profile, but this returns no results. What am I doing wrong?
Update 1
Previously, I was running MongoDB 3.0.9. I've since upgraded to 3.2.3, however I'm still getting the same results. According to the docs, I should be able to query on an embedded document field value.
Update 2
The aggregate function solution works as expected, but since I already had an array of LevelOfNeed objects, I was able to use that to get to the related documents I needed using the $in operator.
Unfortunately mongodb does not support joins until version 3.2. In version 3.2 it provides the $lookup aggregation operator to lookup referenced documents across collections.
You could use it as below:
db.Profile.aggregate([
{
$lookup:{
"from":"LevelOfNeed",
"localField":"levelOfNeedServiced",
"foreignField":"_id",
"as":"joined"
}
},
{
$match:{
"joined.sortOrder":{$gte:2}
}
},
{
$project:{"levelOfNeedServiced":1,...} //include things you want to project.
}
])
Your code:
db.getCollection('profiles').find({
'levelOfNeedServiced.sortOrder': { $gte: 2 }
})
does not work as intended because, the field levelOfNeedServiced is identified as a field containing an ObjectId and not the resolved LevelOfNeed document.