0

How to query an object from an Array inside an Array, and get it as a top-level object? For example, consider the following record.

{
  "subjects": [
         {
             "name": "English",
             "teachers": [
                  {
                    "name": "Mark"   /* Trying to get this object*/
                  },
                  {
                    "name": "John"
                  }
              ]
         }
   ]
}

I am trying to get the following object out as the top-level object.

{
   "name": "Mark"
}

1 Answer 1

1

You need to use the aggregation framework to do exactly what you're asking for.

Here I entered the document you gave into collection: foo.

> db.foo.find().pretty()

{
    "_id" : ObjectId("57ceed3d31484d5b491eaae9"),
    "subjects" : [
        {
            "name" : "English",
            "teachers" : [
                {
                    "name" : "Mark"
                },
                {
                    "name" : "John"
                }
            ]
        }
    ]
}

Using $unwind to unravel our array we then enter our first stage of the aggregation pipeline:

> db.foo.aggregate([
...     {$unwind: "$subjects"}
... ]).pretty()

{
    "_id" : ObjectId("57ceed3d31484d5b491eaae9"),
    "subjects" : {
        "name" : "English",
        "teachers" : [
            {
                "name" : "Mark"
            },
            {
                "name" : "John"
            }
        ]
    }
}

Subjects was an array of length 1 so the only difference here is one less set of [] array brackets.

We need to unwind again.

> db.foo.aggregate([
...     {$unwind: "$subjects"},
...     {$unwind: "$subjects.teachers"}
... ]).pretty()

{
    "_id" : ObjectId("57ceed3d31484d5b491eaae9"),
    "subjects" : {
        "name" : "English",
        "teachers" : {
            "name" : "Mark"
        }
    }
}
{
    "_id" : ObjectId("57ceed3d31484d5b491eaae9"),
    "subjects" : {
        "name" : "English",
        "teachers" : {
            "name" : "John"
        }
    }
}

Now we turned our array of length '2' into two separate documents. The first one with subjects.teachers.name = Mark and the second with subjects.teachers.name = John.

We only want to return the case where name = Mark so we need to add a $match stage to our pipeline.

> db.foo.aggregate([
...     {$unwind: "$subjects"},
...     {$unwind: "$subjects.teachers"},
...     {$match: {"subjects.teachers.name": "Mark"}}
... ]).pretty()

{
    "_id" : ObjectId("57ceed3d31484d5b491eaae9"),
    "subjects" : {
        "name" : "English",
        "teachers" : {
            "name" : "Mark"
        }
    }
}

Ok! Now we are only matching on the case where name: Mark.

Let's add a $project case to shape our input how we want.

> db.foo.aggregate([
...     {$unwind: "$subjects"},
...     {$unwind: "$subjects.teachers"},
...     {$match: {"subjects.teachers.name": "Mark"}},
...     {$project: {"name": "$subjects.teachers.name", "_id": 0}}
... ]).pretty()

{ "name" : "Mark" }
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much for the detailed answered.
You have to use docs.mongodb.com/manual/reference/operator/aggregation/literal/… For instance: {$project: {"name": "$subjects.teachers.name", "_id": 0, "address": {$literal: "123 ABC"}}}
No. I mean to update the information into the collection. So that the collection becomes: { "subjects": [ { "name": "English", "teachers": [ { "name": "Mark" , "address": "123 ABC XYZ" }, { "name": "John" } ] } ] }
Aggregation isn't really a write operation in the Mongo Shell. You'd have to execute this inside your server code and pass the results into a callback function where you can then do your manipulation and resave the documents.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.