Reputation: 4561
How can I select all items in an embedded documents in mongodb? for example, how can I select all the items inside books ?
{
"_id": 67
"Item_1":
{
"books": [
{"genre": "Classics", "author": "something"},
{"genre": "Fiction", "author": "something"}
{"genre": "Fiction", "author": "something"}
]¨
}
}
Upvotes: 0
Views: 109
Reputation: 151112
If you are looking to "flatten" out your schema a little then you can do this with the aggregation framework and use of the $project
pipeline operator. This has a few more features than the projection available to .find()
and in this sample you also use $unwind
to "de-normalize" your arrays as documents:
db.collection.aggregate([
{ "$unwind": "$Item_1.books" },
{ "$project": {
"genre": "$Item_1.books.genre", "author": "$Item_1.books.author"
}}
])
That gives you a result like this:
{ "_id": 67, "genre": "Classics", "author": "something" },
{ "_id": 67, "genre": "Fiction", "author": "something" }
{ "_id": 67, "genre": "Fiction", "author": "something" }
It does make a certain amount of sense to keep something referring to the original document in there and _id
will be there unless you exclude it. But you can always exclude if your really want.
Additionally you can filter this list for multiple results, for example just to get the "Fiction" genre:
db.collection.aggregate([
{ "$unwind": "$Item_1.books" },
{ "$match": { "Item_1.books.genre": "Fiction" } }
{ "$project": {
"genre": "$Item_1.books.genre", "author": "$Item_1.books.author"
}}
])
Gives you:
{ "_id": 67, "genre": "Fiction", "author": "something" }
{ "_id": 67, "genre": "Fiction", "author": "something" }
Over multiple documents it may also make sense to use an additional $match
first in the pipeline, in order to reduce the number of documents that did not contain a "Fiction" genre in their array.
Another form with MongoDB 2.6 may also be to do this:
db.collection.aggregate([
{ "$match": { "Item_1.books.genre": "Fiction" } },
{ "$project": {
"books": {
"$setDifference: [
{ "$map": {
"input ": "$Item_1.books",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.genre", "Fiction" ] },
"$$el",
false
]
}
}},
[false]
]
}
}},
{ "$unwind": "$books" },
{ "$project": {
"genre": "$books.genre",
"author": "$books.fiction"
}}
])
This moves the $match
on the array elements to an "inline" version to filter the contents of the array using $map
and $setDifference
. It is just another approach and utility may vary considering the size of the array, where small sizes will make little difference.
As a final note, your schema appears to be keyed off of that "Item_1" key there as an object. If it is your actual intent to have many of these and indeed search or combine results across them, then a schema change would greatly benefit you in querying:
{
"_id": 67
"books": [
{"type": "Item1", "genre": "Classics", "author": "something"},
{"type": "Item1", "genre": "Fiction", "author": "something"},
{"type": "Item1", "genre": "Fiction", "author": "something"},
{"type": "Item2", "genre": "Fiction", "author": "something"}
]¨
}
That makes it easy to combine across the various keys or indeed separate or group without having to specify directly down the path to a field as you would presently have to do.
Upvotes: 1