【问题标题】:Using MongoDB's positional operator $ in a deeply nested document query在深度嵌套的文档查询中使用 MongoDB 位置运算符 $
【发布时间】:2014-12-27 21:07:41
【问题描述】:

是否可以将位置运算符“$”与深度嵌套文档数组的查询结合使用?

考虑以下定义“用户”的嵌套文档:

{
   username: 'test',
   kingdoms: [

      {
          buildings: [

              {
                  type: 'castle'

              },
              {
                  type: 'treasury'
              },

              ...

          ]

      },

      ...

   ] 
}

我们想为特定用户返回“城堡”,例如形式:

{
    kingdoms: [{

        buildings: [{

            type: 'castle'

        }]

    }]
}

因为你不能使用 $ 运算符两次 (https://jira.mongodb.org/browse/server-831) 我知道我也不能查询特定的王国,所以我正在尝试为第 n 个王国编写查找语句。

在更新深度嵌套的子文档 (Mongodb update deeply nested subdocument) 时,这似乎是有道理的,但我在查找查询方面的成功率较低。

我可以通过查询返回第一个王国的建筑:

db.users.findOne(
    { username: 'test' },
    { kingdoms: {$slice: [0, 1]}, 'kingdom.buildings': 1 }
);

但这会返回该王国的所有建筑物。

按照位置运算符的单级示例,我正在尝试这样的查询:

db.users.findOne(
    { username: 'test', 'kingdoms.buildings.type': 'castle' },
    { kingdoms: {$slice: [n, 1]}, 'kingdom.buildings.$': 1 }
);

这样的形式:

db.collection.find( { <array.field>: <value> ...}, { "<array>.$": 1 } ) 

如文档http://docs.mongodb.org/manual/reference/operator/projection/positional/#proj.S中所述

但是这失败并出现错误:

Positional operator does not match the query specifier

大概是因为 Kingdoms.buildings 不被视为一个数组。我也试过 kingdoms.0.buildings

这令人困惑,因为这似乎适用于更新(根据Mongodb update deeply nested subdocument

是我的语法错误还是不支持?如果是这样,有没有办法实现类似的目标?

【问题讨论】:

    标签: mongodb nested positional-operator


    【解决方案1】:

    你从

    得到一个错误
    db.users.findOne(
        { username: 'test', 'kingdoms.buildings.type': 'castle' },
        { kingdoms: {$slice: [n, 1]}, 'kingdom.buildings.$': 1 }
    );
    

    因为有一个拼写错误(“kingdom.buildings.$”应该是“kingdoms.buildings.$”)。
    然而,这种方式并不能达到你的预期。
    $总是针对kingdoms.buildings路径中的kingdoms——第一个数组。

    这种方法应该可以解决问题。
    (需要 V2.6+)

    db.c.aggregate([ {
        $match : {
            username : 'test',
            'kingdoms.buildings.type' : 'castle'
        }
    }, {
        $project : {
            _id : 0,
            kingdoms : 1
        }
    }, {
        $redact : {
            $cond : {
                "if" : {
                    $or : [ {
                        $gt : [ "$kingdoms", [] ]
                    }, {
                        $gt : [ "$buildings", [] ]
                    }, {
                        $eq : [ "$type", "castle" ]
                    } ]
                },
                "then" : "$$DESCEND",
                "else" : "$$PRUNE"
            }
        }
    } ]).pretty();
    

    只保留王国的第一个元素,

    db.c.aggregate([ {
        $match : {
            username : 'test',
            'kingdoms.buildings.type' : 'castle'
        }
    }, {
        $redact : {
            $cond : {
                "if" : {
                    $or : [ {
                        $gt : [ "$kingdoms", [] ]
                    }, {
                        $gt : [ "$buildings", [] ]
                    }, {
                        $eq : [ "$type", "castle" ]
                    } ]
                },
                "then" : "$$DESCEND",
                "else" : "$$PRUNE"
            }
        }
    }, {
        $unwind : "$kingdoms"
    }, {
        $group : {
            _id : "$_id",
            kingdom : {
                $first : "$kingdoms"
            }
        }
    }, {
        $group : {
            _id : "$_id",
            kingdoms : {
                $push : "$kingdom"
            }
        }
    }, {
        $project : {
            _id : 0,
            kingdoms : 1
        }
    } ]).pretty();
    

    【讨论】:

    • 感谢向导 - 是否可以简单地在层次结构的不同级别指定这些条件?假设一个王国有一个密钥type: "castle",那么即使它与建筑物的类型不匹配,它也会 $$DESCEND ? (如果该示例无效,则为类似情况)
    • @Hugheth,不太清楚你的观点。你能发布一个文件来描述它吗?
    • 这个答案和 my 用例之间的主要区别在于它不会过滤到单个王国 - 而是返回用户的所有城堡
    • @Hugheth,您的意思是您只想返回 kingdoms 字段的一个元素吗?那你要回哪一个,第一个呢?
    • 是的,虽然最好有一个关于第 n 个王国的一般答案,即。任何特定的王国
    猜你喜欢
    • 1970-01-01
    • 2018-03-09
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    • 2016-08-16
    • 2013-08-12
    • 1970-01-01
    相关资源
    最近更新 更多