【问题标题】:Retrieve n-level deep sub-document in MongoDB在 MongoDB 中检索 n 级深度子文档
【发布时间】:2012-11-15 13:42:33
【问题描述】:

我在 mongoDB 中有一个深度嵌套的文档,我想获取单个子对象。

例子:

{
   "schoolName": "Cool School",
   "principal": "Joe Banks",
   "rooms": [
      {
         "number": 100
         "teacher": "Alvin Melvin"
         "students": [
            {
               "name": "Bort"
               "currentGrade": "A"
            },
            // ... many more students
         ]
      },
      // ... many more rooms
   ]
}

最近 Mongo 更新为允许使用 $elemMatch projection 进行 1 级深度子对象检索:

var projection = { _id: 0, rooms: { $elemMatch: { number: 100 } } };
db.schools.find({"schoolName": "Cool School"}, projection);
// returns { "rooms": [ /* array containing only the matching room */ ]  }

但是当我尝试以同样的方式获取一个学生(2 级深度)时,我收到一个错误:

var projection = { _id: 0, "rooms.students": { $elemMatch: { name: "Bort" } } };
db.schools.find({"schoolName": "Cool School"}, projection);
// "$err": "Cannot use $elemMatch projection on a nested field (currently unsupported).", "code": 16344

有没有办法在 mongoDB 文档中检索任意深度的子对象?

我正在使用 Mongo 2.2.1

【问题讨论】:

  • 位置运算符或$elemMatch 不支持匹配任意嵌套的子文档。您可以投票/观看SERVER-831,这是“位置运算符匹配嵌套数组”的功能请求。

标签: javascript mongodb


【解决方案1】:

我最近问了一个类似的问题,可以提供一个适当的一般性答案(请参阅Using MongoDB's positional operator $ in a deeply nested document query

此方案仅支持 Mongo 2.6+,但从那时起您可以使用聚合框架的 $redact 函数。

这是一个示例查询,它应该只返回您的学生 Bort。

db.users.aggregate({

    $match: { schoolName: 'Cool School' }

}, {

    $project: {

        _id: 0,
        'schoolName': 1,
        'rooms.number': 1,
        'rooms.students': 1

    }

}, {

    $redact: {
        $cond: {
            "if": {
                $or: [{
                    $gte: ['$schoolName', '']
                }, {
                    $eq: ['$number', 100]
                }]
            },
            "then": "$$DESCEND",
            "else": {
                $cond: {
                    "if": {
                        $eq: ['$name', 'Bort']
                    },
                    "then": "$$KEEP",
                    "else": "$$PRUNE"
                }
            }
        }
    }

});

$redact 可用于通过在匹配的文档中递归地匹配或修剪子文档来进行子查询。

You can read about $redact here 了解更多关于正在发生的事情,但我确定的设计模式具有以下要求:

  • 编校条件适用于 每个 子文档级别,因此您需要在每个级别都有一个唯一字段,例如您不能将数字作为房间的钥匙,学生说
  • 它仅适用于数据字段而不是数组索引,因此如果您想知道嵌套文档 (for example to update it) 的返回位置,您需要在文档中包含并维护它
  • $redact 中 $or 语句的每个部分都应与您想要的特定级别的文档相匹配
  • 因此,$or 语句的每个部分都需要包含与该级别文档的唯一字段的匹配项。例如,$eq: ['$number', 100] 匹配编号为 100 的房间
  • 如果您没有在某个级别指定查询,则仍需要包含唯一字段。例如,如果它是一个字符串,您可以将其与$gte: ['$uniqueField': ''] 匹配
  • 最后一个文档级别位于 second if 表达式中,以便保留该文档的所有内容。

【讨论】:

    【解决方案2】:

    我目前手边没有 mongodb 2.2,所以无法测试,但你试过了吗?

    var projection = { _id: 0, rooms: { $elemMatch: { "students.name": "Bort" } } };
    db.schools.find({"schoolName": "Cool School"}, projection);
    

    【讨论】:

    • 啊,该查询匹配但不幸的是返回包含“Bort”的“房间”对象,我想要“学生”对象。
    • 是的,听起来它还不支持。 github.com/mongodb/mongo/blob/master/src/mongo/db/…(很讨厌! mongoutils::str::contains( e.fieldName(), '.' )
    猜你喜欢
    • 2017-01-11
    • 2018-01-19
    • 2015-12-07
    • 2016-12-29
    • 2013-08-12
    • 1970-01-01
    • 2018-04-05
    • 2017-06-27
    • 2015-05-25
    相关资源
    最近更新 更多