Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB 3.4 - get array of subdocuments without root document

I have a projects collection with documents such as this:

{
        "_id" : ObjectId("589eff3fee3d13019843f55a"),
        "name" : "Project A",
        "desc" : "test",
        "numofvms" : 0,
        "templates" : [
                {
                        "_id" : ObjectId("589e4c14ee3d131bac8b403c")
                },
                {
                        "_id" : ObjectId("589e4c1dee3d131bac8b403d")
                }
        ],
        "nodes" : [
                {
                        "_id" : ObjectId("589eff8f2bb59057c3f9b89d"),
                        "name" : "Node A"
                },
                {
                        "_id" : ObjectId("589eff962bb59057c3f9b89e"),
                        "name" : "Node B"
                },
                {
                        "_id" : ObjectId("589eff982bb59057c3f9b89f"),
                        "name" : "Node C"
                },
                {
                        "_id" : ObjectId("589eff9a2bb59057c3f9b8a0"),
                        "name" : "Node D"
                }
        ],
        "links" : [ ]
}

I try to receive the array of subdocuments (nodes field) for a given document (project), but without the root document. The result should look like this:

[
        {
                "_id" : ObjectId("589eff8f2bb59057c3f9b89d"),
                "name" : "Node A"
        },
        {
                "_id" : ObjectId("589eff962bb59057c3f9b89e"),
                "name" : "Node B"
        },
        {
                "_id" : ObjectId("589eff982bb59057c3f9b89f"),
                "name" : "Node C"
        },
        {
                "_id" : ObjectId("589eff9a2bb59057c3f9b8a0"),
                "name" : "Node D"
        }
]

I tried different approaches like the one below but could not get rid of the root document:

> db.projects.find({_id: ObjectId("589eff3fee3d13019843f55a")}, { "nodes": 1, _id: 0 }).pretty()
{
        "nodes" : [
                {
                        "_id" : ObjectId("589eff8f2bb59057c3f9b89d"),
                        "name" : "Node A"
                },
                {
                        "_id" : ObjectId("589eff962bb59057c3f9b89e"),
                        "name" : "Node B"
                },
                {
                        "_id" : ObjectId("589eff982bb59057c3f9b89f"),
                        "name" : "Node C"
                },
                {
                        "_id" : ObjectId("589eff9a2bb59057c3f9b8a0"),
                        "name" : "Node D"
                }
        ]
}

Can this be achieved directly in the query or do I have to extract the array manually in my application?

like image 236
NoBodyIsPerfect Avatar asked Feb 11 '17 12:02

NoBodyIsPerfect


1 Answers

Try this:

db.collection.aggregate([
    {
        $unwind: '$nodes'
    },
    {
        $match: {_id: ObjectId("589eff3fee3d13019843f55a") }
    },
    {
        $replaceRoot: { newRoot: "$nodes" }
    }
]).toArray();

And you will get

[
    {
        "_id" : ObjectId("589eff8f2bb59057c3f9b89d"),
        "name" : "Node A"
    },
    {
        "_id" : ObjectId("589eff962bb59057c3f9b89e"),
        "name" : "Node B"
    },
    {
        "_id" : ObjectId("589eff982bb59057c3f9b89f"),
        "name" : "Node C"
    },
    {
        "_id" : ObjectId("589eff9a2bb59057c3f9b8a0"),
        "name" : "Node D"
    }
]

$unwind operator is to deconstruct nodes field from the input documents to output a document for each element.

And then use the $replaceRoot stage to promote the nodes document to the top level, discarding the current top level fields.

Hope this helps.

like image 98
McGrady Avatar answered Sep 29 '22 12:09

McGrady