MongoDB: Retrieving an entire array from a specific document - mongodb

I have set up some test data in mongoDB that has the following form:
{
"_id" : ObjectId("579ab44c0f9f0dc3aeec42ab"),
"name" : "Bob",
"references" : [ 1, 2, 3, 4, 5, 6 ]
}
{
"_id" : ObjectId("579ab7a20f9f0dc3aeec42ac"),
"name" : "Jeff",
"references" : [ 11, 12, 13, 14, 15 ]
}
I want to be able to return the references array only for Bob. Currently I am able to return the complete Document for Bob with the following query:
db.test_2.find({"name" : "Bob"}, bob).pretty()
Basically the general question is how to return an array for a single document in a collection in MongoDB? If I could get any help for this that would be much appreciated!

You can add a projection document to limit the fields returned.
For example:
db.products.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 } )
Take a look at the documentation:
https://docs.mongodb.com/manual/reference/method/db.collection.find/#db.collection.find
The other option would be to select the field from the given document (if you use it in a loop for example).
In any case mongo will return a json document which you need to take the array from.
Regards
Jony

You can do this...
db.test_2.findOne({ "name": "Bob" }).select({ references: 1, _id: 1 })
P.S this is with MongoDB v4.2

db.test_2.find({ "name": "Bob" }, { "references": 1 });

Related

Mongodb- using find() method on an Array of Objects only return first match instead of all

Unlike the other question someone asked where they wanted only one item returned. I HAVE one item returned and I need ALL of the matching objects in the array return. However the second object that matches my query is being completely ignored.
This is what one of the items in the item collection looks like:
{
name: "soda",
cost: .50,
inventory: [
{ flavor: "Grape",
amount: 8 },
{ flavor: "Orange",
amount: 4 },
{ flavor: "Root Beer",
amount: 15 }
]
}
Here is the query I typed in to mongo shell:
Items.find({"inventory.amount" : { $lte : 10} } , { name : 1, "inventory.$.flavor" : 1})
And here is the result:
"_id" : ObjectId("59dbe33094b70e0b5851724c"),
"name": "soda"
"inventory" : [
{ "flavor" : "Grape",
"amount" : 8,
}
]
And here is what I want it to return to me:
"_id" : ObjectId("59dbe33094b70e0b5851724c"),
"name": "soda"
"inventory" : [
{ "flavor" : "Grape",
"amount" : 8
},
{ "flavor" : "Orange",
"amount" : 4
}
]
I'm new to mongo and am dabbling to get familiar with it. I've read through the docs but couldn't find a solution to this though it's quite possible I overlooked it. I'd really love some help. Thanks in advance.
first u can get your result by this query
db.Items.find({"inventory.amount" : { $lte : 10} } , { name : 1, "inventory.flavor" : 1 , "inventory.amount" : 1})

Increment nested value

I create players the following way.
Players.insert({
name: name,
score: 0,
items: [{'name': 0}, {'name2': 0}...]
});
How do I increment the score in a specific player and specific item name (upserting if necessary)?
Sorry for the terrible wording :p
Well, the answer is - as in life - to simplify the problem by breaking it up.
And to avoid arrays in mongoDB - after all, objects can have as many keys as you like. So, my structure became:
{
"_id": <id>,
"name": <name>,
"score": <score>,
"items": {}
}
And to increment the a dynamic key in items:
// create your update skeleton first
var ud = { $inc: {} };
// fill it in
ud.$inc['item.' + key] = value;
// call it
db.Players.update(player, ud, true);
Works a charm :)
Lets say you have:
{
"_id" : ObjectId("5465332e6c3e2eeb66ef3683"),
"name" : "Alex",
"score" : 0,
"items" : [
{
"food" : 0
}
]
}
To update you can do:
db.Players.update({name: "Alex", "items.food": {$exists : true}},
{$inc: {score: 1, "items.$.food": 5}})
Result:
{
"_id" : ObjectId("5465332e6c3e2eeb66ef3683"),
"name" : "Alex",
"score" : 1,
"items" : [
{
"food" : 5
}
]
}
I am not sure you can upsert if the document doesn't exist because of the positional operator needed to update the array.

Nested Query Mongodb

I have this beautiful Json , and I'm trying with the powerful mongodb query to get all comments with file_id 12....so this is what I would like have back [4,5,7,10,11,15].
I tried with this query but the file_id it's completely ignored by the engine:
db.collection.distinct("changes.comments",
{"my_uuid":"bf48e757-1a65-4546-bf24-2bb001effddd",
"changes":{$elemMatch:{file_id:12}} }
)
Output:
{
"_id" : ObjectId("5342bf796b03d7ffc834afcc"),
"my_uuid" : "bf48e757-1a65-4546-bf24-2bb001effddd",
"changes" : [
{
"file_id" : 12,
"version" : 1,
"comments" : [
4,
5,
7
],
"lastseen" : 1394640549
},
{
"file_id" : 12,
"version" : 2,
"comments" : [
10,
11,
15
],
"lastseen" : 1394640511
},
{
"file_id" : 234,
"version" : 1,
"comments" : [
100,
110,
150
],
"lastseen" : 1394640555
}
]
}
Thanks in advance
You can use the aggregation framework to achieve what you what. Although the query looks complex for what you are trying to do, it's simple once you get a hang of it.
db.collection.aggregate([
// Get only documents where "my_uuid" equals "bf48e757-1a65-4546-bf24-2bb001effddd"
{"$match":{"my_uuid":"bf48e757-1a65-4546-bf24-2bb001effddd"}},
// Unwind the "changes" array
{"$unwind":"$changes"},
// Get only elements of the "changes" array where "file_id" equals 12
{"$match":{"changes.file_id":12}},
// Unwind the "comments" array
{"$unwind":"$changes.comments"},
// Group by _id and add the comments to array only if not already present
{"$group":{_id:"$_id", comments:{$addToSet:"$changes.comments"}}},
// Cleanup the output
{"$project":{_id:0, comments:1}}
])
Output:
{
"result" : [
{
"comments" : [
4,
5,
7,
10,
11,
15
]
}
],
"ok" : 1
}
EDIT: Including my_uuid in the results is fairly straight-forward. We just need to group by my_uuid instead of _id:
db.collection.aggregate([
{"$match":{"my_uuid":"bf48e757-1a65-4546-bf24-2bb001effddd"}},
{"$unwind":"$changes"},
{"$match":{"changes.file_id":12}},
{"$unwind":"$changes.comments"},
{"$group":{_id:"$my_uuid", comments:{$addToSet:"$changes.comments"}}},
{"$project":{_id:0, my_uuid:"$_id", comments:1}}
])
Currently there is no straight forward way of pulling out only the matching document from an array. The $elemMatch operator will only ensure that at least one of the documents within the array satisfies the condition provided by you. The query will however, always return the entire document. One way to achieve what you are looking for is -
db.sample4.aggregate({$unwind:"$changes"},{$match:{"changes.file_id":12}},{$project:{"changes.comments":1,"_id":0}});
These topics are covered here in stackoverflow, where map-reduce approach as well is listed to achieve this. If the requirement was to return the first matching document, the you could have projected using changes.comments.$:1 Eg. - db.sample4.find({"changes":{$elemMatch:{"file_id":12}} },{"changes.comments.$":1} )

MongoDB: Missing fields after sort() when using projection

So I have a database filled with image information, and I want to retrieve a subset of the fields sorted by ascending date. I use the following query to retrieve the aggregated set:
db.images.find({}, {rel_path: 1, date: 1}).sort({'date.year': 1, 'date.month': 1})
I expect this query to return a set looking something like this:
{
"_id": ObjectId("530deb1060832c64291a11a7"),
"date": { "year: 2006, "month": 2 },
"rel_path": "/mnt/backup/Backup/Photos/asdfasdfasdf.jpg"
}
{
"_id": ObjectId("530de1db60832c64291a05ec"),
"date": { "year: 2006, "month": 5 },
"rel_path": "/mnt/backup/Backup/Photos/qweqweqwe.jpg"
}
... <more documents> ...
What I get, however, looks like this:
{
"_id": ObjectId("530deb1060832c64291a11a7"),
"rel_path": "/mnt/backup/Backup/Photos/asdfasdfasdf.jpg"
}
{
"_id": ObjectId("530de1db60832c64291a05ec"),
"rel_path": "/mnt/backup/Backup/Photos/qweqweqwe.jpg"
}
... <more documents> ...
If I skip the 'sort()' I get all fields from my projection, so it seems the 'date' field somehow is removed by the 'sort()' call.
Anyone have any idea what's going on here?
Edit: Here's a sample document by request:
{
"_id" : ObjectId("530de16860832c64291a0562"),
"orientation" : 1,
"camera_make" : "Apple",
"camera_model" : "iPhone 4",
"rel_path" : "Bröllopsbilder/IMG_0997.JPG",
"file_size" : 1827977,
"date" : { "month" : "10", "year" : "2011" },
"root" : "/mnt/backup/Backup/Bilder/",
"md5" : "fb26ebf24914d515144be5e53797744b"
}
The find() query looks fine and it works as expected. I tested it by running it against a similar data set.
Reason this could be happening is when a few documents in the collection do not have the "date" field. Try running the same query by adding a filter criteria in the find query to return only those results where "date" field exists using $exists operator i.e.,:
db.images.find({date:{$exists:true}}, {rel_path: 1, date: 1})
.sort({'date.year': 1, 'date.month': 1})

How to query recent comments in Mongodb

The the post document looks like this:
{
...
comments: [{
_id:...
body:...
createDate:...
},
...
]
}
How do I get recent 10 comments from the collection?
If your comments are always in a predictable order (i.e. newest first, or newest last), then you can use the $slice operator to return just a subset of the full comments field when querying:
test> db.foo.save({name: "hello", comments: [1, 2, 3, 4, 5]})
test> db.foo.find({}, {comments: {$slice: 3}})
{ "_id" : ObjectId("4ec7d1c8e72da9b6f31e2528"), "name" : "hello", "comments" : [ 1, 2, 3 ] }
test> db.foo.find({}, {comments: {$slice: -3}})
{ "_id" : ObjectId("4ec7d1c8e72da9b6f31e2528"), "name" : "hello", "comments" : [ 3, 4, 5 ] }
You can read more about controlling the returned fields at http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
There is no way to partially select the items from embedded document. No matter what it will return the entire array of document. You have to do the filter in your application code. Thats the only way.
But i recommend to have a separate collection for comments. That way you can skip & limit the set.