【问题标题】:How do I find an object nested deep in arrays in MongoDB?如何在 MongoDB 中找到嵌套在数组深处的对象?
【发布时间】:2016-09-24 15:00:05
【问题描述】:

我是 MongoDB 新手,在为我正在进行的新项目设置特定查询时遇到了一些困难。

我有一个看起来像这样的数据结构(简化版):

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

(即游戏包含场景集合,场景包含视图集合)。

我在这里要查询的是一个特定的视图对象。我想答案涉及使用 $elemMatch,但我该如何设置呢?经过一些研究+玩耍,我知道我可以这样做来获得场景:

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

但是我如何扩展它以便它只提取我感兴趣的特定视图(通过_id)?

我想我总是可以使用 for 循环手动找到我正在寻找的视图对象,这带来了另一个问题。 Wrt 性能,最好使用 Mongo 进行这样的查询,还是手动拉整个文档以循环遍历集合?

【问题讨论】:

  • 不能只获取数组中的对象。您必须提取整个文档。如果您想要自己的子文档,则需要使用聚合框架。
  • @cdbajorin 当您说“自己”时,您的意思是不可能过滤掉带有我不想要的 _id 值的视图吗?还是您的意思是我正在寻找的查询会给我一个类似result.scenes[0].views[0] 的结构?我同意后者。

标签: node.js mongodb


【解决方案1】:

如果你的集合不大,而且这种操作比较少见,那么用聚合框架来做可能没问题。但是,如果此操作频繁且对性能至关重要,那么我会说使用应用程序级查询。无论如何,这就是您使用聚合和$unwind 的方式:

如果这是你的收藏:

> 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"
                        }
                    ]
                }
            ]
        }
    ]
}

假设你想找到 ID 为 "view112" 的视图,你可以这样做:

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

你会得到:

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

【讨论】:

  • 这很酷。这是否要求此特定视图 ID 不存在于任何其他场景中?有关为此查询创建索引的任何提示?
  • 如果此视图 ID 将出现在其他位置,您将在响应中获得多个元素。话虽如此,您可以向 $match 指令添加条件,以便专注于特定的游戏、视图、场景等。至于您的其他问题,索引在这里无济于事,因为匹配是在一个不是原始集合,但是所有unwinds 的结果,因此无法对其进行索引
  • 因为您首先知道视图 ID,所以您应该在聚合顶部有一个 $match 以仅过滤具有该视图的游戏。
【解决方案2】:

不明白,你可以直接用查询

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

它应该从场景中删除其他场景(双关语)。但是是的,它仍然会为您提供多个视图,因为 $ 在任何表达式中只能使用一次。我认为循环是关键。

我建议不要聚合,它们的成本很高。至于通过查询来实现这一点或获取文档并循环它。我肯定会通过查询来实现它,你获取的数据越少性能越好。

【讨论】:

  • 您的意思是比较 scene.views._id 和 req.params.vid(与 sid 相对,这是我用于场景的参数)? _id:0 有什么作用?请注意,视图 ID 也是 ObjectId 对象。
  • 是的,它应该是 req.params.vid,我的错,更新了答案。如果是object Id,则保证唯一。
  • 对于第二部分,_id:0 告诉我们在结果中不需要 _id。这是投影策略。你可以分别用 1 和 0 来设置你想要什么和不想要什么。
猜你喜欢
  • 2019-02-17
  • 2020-09-24
  • 2021-01-18
  • 1970-01-01
  • 1970-01-01
  • 2021-06-25
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多