Reputation: 95
I have a question about querying nested documents. I tried to search but nothing answered my question or I am maybe overlooking it. I have structure like this:
{
"_id" : ObjectId("592aa441e0f8de09b0912fe9"),
"name" : "Patrick Rothfuss",
"books" : [
{
"title" : "Name of the wind",
"pages" : 400,
"_id" : ObjectId("592aa441e0f8de09b0912fea")
},
{
"title" : "Wise Man's Fear",
"pages" : 500,
"_id" : ObjectId("592aa441e0f8de09b0912feb")
},
},
{
"_id" : ObjectId("592aa441e0f8de09b0912fe9"),
"name" : "Rober Jordan",
"books" : [
{
"title" : "The Eye of the World",
"pages" : 400,
"_id" : ObjectId("592aa441e0f8de09b0912fea")
},
{
"title" : "The Great Hunt",
"pages" : 500,
"_id" : ObjectId("592aa441e0f8de09b0912feb")
}
},
And I would like to query for the list of all books in entire colletion of Authors - something like:
"books" : [
{
"title" : "The Eye of the World",
"pages" : 400,
"_id" : ObjectId("592aa441e0f8de09b0912fea")
},
{
"title" : "The Great Hunt",
"pages" : 500,
"_id" : ObjectId("592aa441e0f8de09b0912feb")
},
{
"title" : "Name of the wind",
"pages" : 400,
"_id" : ObjectId("592aa441e0f8de09b0912fea")
},
{
"title" : "Wise Man's Fear",
"pages" : 500,
"_id" : ObjectId("592aa441e0f8de09b0912fea")
}]
Upvotes: 5
Views: 2575
Reputation: 151082
You can do this using .aggregate()
and predominantly the $unwind
pipeline operator:
In modern MongoDB 3.4 and above you can use in tandem with $replaceRoot
Model.aggregate([
{ "$unwind": "$books" },
{ "$replaceRoot": { "newRoot": "$books" } }
],function(err,results) {
})
In earlier versions you specify all fields with $project
:
Model.aggregate([
{ "$unwind": "$books" },
{ "$project": {
"_id": "$books._id",
"pages": "$books.pages",
"title": "$books.title"
}}
],function(err,results) {
})
So $unwind
is what you use to deconstruct or "denormalise" the array entries for processing. Effectively this creates a copy of the whole document for each member of the array.
The rest of the task is about returning "only" those fields present in the array.
It's not a very wise thing to do though. If your intent is to only return content embedded within an array of a document, then you would be better off putting that content into a separate collection instead.
It's far better for performance, pulling apart a all documents from a collection with the aggregation framework, just to list those documents from the array only.
Upvotes: 9
Reputation: 3845
According to above mentioned description please try executing following query in MongoDB shell.
db.collection.aggregate(
// Pipeline
[
// Stage 1
{
$unwind: "$books"
},
// Stage 2
{
$group: {
_id:null,
books:{$addToSet:'$books'}
}
},
// Stage 3
{
$project: {
books:1,
_id:0
}
},
]
);
Upvotes: 0