torbonde
torbonde

Reputation: 2459

Selecting part of tree in MongoDB

I am storing some tree-structured data in MongoDB. It looks something like this:

{
    "_id": 1,
    "name": "foo",
    "subs": [{
        "name": "bar",
        "subs": [{
            "name": "baz"
        },
        {
            "name": "gizmo"
        }]
    }]
}
{
    "_id": 2,
    "name": "foo",
    "subs": [{
        "name": "bar",
        "subs": [{
            "name": "gizmo"
        }]
    }]
}

I want to query on the subtrees, and wish only to extract the part of the tree that matches the query. In each node, there is at most one subtree that matches the query.

In this test-query, I want to find the part of the tree that matches the path "foo/bar/gizmo". So, I have tried the following:

db.Test.find({
    name: "foo",
    subs: {
        $elemMatch: {
            name: "bar",
            subs: {
                $elemMatch: {
                    name: "gizmo"
                }
            }
        }
    }
},
{
    "subs.subs": {
        $slice: 1
    }
})

The result I get is

{
    "_id": 1,
    "name": "foo",
    "subs": [{
        "name": "bar",
        "subs": [{
            "name": "baz"
        }]
    }]
}{
    "_id": 2,
    "name": "foo",
    "subs": [{
        "name": "bar",
        "subs": [{
            "name": "gizmo"
        }]
    }]
}

whereas I actually wanted

{
    "_id": 1,
    "name": "foo",
    "subs": [{
        "name": "bar",
        "subs": [{
            "name": "gizmo"
        }]
    }]
}{
    "_id": 2,
    "name": "foo",
    "subs": [{
        "name": "bar",
        "subs": [{
            "name": "gizmo"
        }]
    }]
}

Is it possible to do this? I have tried using the $elemMatch projection operator on the subtrees, but it seems it is not currently supported by Mongo (Cannot use $elemMatch projection on a nested field (currently unsupported)).

Upvotes: 1

Views: 575

Answers (1)

Parvin Gasimzade
Parvin Gasimzade

Reputation: 26012

You can use Aggregation Framework to get result as you wish. Try the following code :

db.Test.aggregate(
{$unwind : "$subs"},
{$unwind : "$subs.subs"},
{$match : {name : "foo", "subs.name" : "bar", "subs.subs.name" : "gizmo"}}
);

Result will be like :

"result" : [
    {
        "_id" : ObjectId("52540da2e3a2c44e082642d4"),
        "name" : "foo",
        "subs" : {
            "name" : "bar",
            "subs" : {
                "name" : "gizmo"
            }
        }
    },
    {
        "_id" : ObjectId("52540dd6e3a2c44e082642d5"),
        "name" : "foo",
        "subs" : {
            "name" : "bar",
            "subs" : {
                "name" : "gizmo"
            }
        }
    }
]

Upvotes: 0

Related Questions