简体   繁体   中英

How do I find an object nested deep in arrays in MongoDB?

I'm new to MongoDB and have been having a bit of difficulty setting up a particular query for a new project I'm working on.

I have a data structure that looks like this (simplified version):

games: {_id: ..., scenes: [{_id: ..., views: [{_id: ...}]}]}

(ie games contains a collection of scenes, scenes contains a collection of views).

What I want to query here is a particular view object. I suppose the answer involves using $elemMatch, but how do I set this up? After a bit of research + playing around, I know I can do this to get the scene:

db.collection("games").findOne({
    _id: ObjectId(req.params.gid)}, 
    {
    scenes: {
        $elemMatch: {_id: ObjectId(req.params.sid)}
    }
}...

But how do I extend this so that it only pulls the particular view I'm interested in (by _id)?

I guess I could always find the view object I'm looking for manually using a for loop, which brings up another question. Wrt performance, is better to do queries like this using Mongo, or manually by pulling the entire document to loop through collections?

If your collection is not big, and this operation is relatively rare, then it may be fine to do it with the aggregation framework. But if this operation is frequent and performance-critical then I'd say go with application-level querying. In any case, this is how you'd do it with aggregation and $unwind :

So if this is your collection:

> db.col.find().pretty()
{
    "_id" : ObjectId("57e6a5404897ec06f1c3d86f"),
    "games" : [
        {
            "_id" : "game1",
            "scenes" : [
                {
                    "_id" : "scene1",
                    "views" : [
                        {
                            "_id" : "view1"
                        }
                    ]
                }
            ]
        }
    ]
}
{
    "_id" : ObjectId("57e6a5d24897ec06f1c3d870"),
    "games" : [
        {
            "_id" : "game1",
            "scenes" : [
                {
                    "_id" : "scene11",
                    "views" : [
                        {
                            "_id" : "view111"
                        },
                        {
                            "_id" : "view112"
                        },
                        {
                            "_id" : "view113"
                        }
                    ]
                },
                {
                    "_id" : "scene12",
                    "views" : [
                        {
                            "_id" : "view121"
                        }
                    ]
                }
            ]
        },
        {
            "_id" : "game2",
            "scenes" : [
                {
                    "_id" : "scene21",
                    "views" : [
                        {
                            "_id" : "view211"
                        }
                    ]
                }
            ]
        }
    ]
}

and let's say you want to find the view with ID "view112" , you can do:

db.col.aggregate([
    { $unwind: "$games"},
    { $unwind: "$games.scenes"},
    { $unwind: "$games.scenes.views"},
    {
        $match: {
            "games.scenes.views._id": "view112"
        }
    }
])

and you'll get:

{
    "_id": ObjectId("57e6a5d24897ec06f1c3d870"),
    "games": {
        "_id": "game1",
        "scenes": {
            "_id": "scene11",
            "views": {
                "_id": "view112"
            }
        }
    }
}

I don't get it, you can query directly with

db.collection("games").findOne({"scenes.views._id":ObjectId(req.params.vid)},{_id:0,"scenes.$.views":1});

It should remove other scenes from the scene (pun intended). But yeah It still would provide you with multiple views since $ can be used only once in any expression. There I think looping is the key.

I would suggest against aggregate, they are costly. As for to achieve this with query or get the document and loop through it. I for sure would go for achieving it through query, the less data you fetch the better the performance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM