5

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")
    }]
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
LadaWalker
  • 95
  • 8
  • What are you asking here exactly? Is is how to just "extract" all of the embedded documents from the array and simply return those results? What does "same name" mean here? – Neil Lunn May 28 '17 at 11:00
  • same elemnt name i guess - **books**? extract the list of every **book** in db collection. Did not know how to describe it better. – LadaWalker May 28 '17 at 11:03

2 Answers2

9

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.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
0

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

    ]

);
Rubin Porwal
  • 3,736
  • 1
  • 23
  • 26