在深度嵌套的文档查询中使用MongoDB的位置运算符$

时间:2014-10-31 13:58:38

标签: mongodb nested positional-operator

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

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

{
   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.0.buildings

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

我是否只是语法错误或者不支持?如果有,有办法实现类似的东西吗?

1 个答案:

答案 0 :(得分:3)

您收到错误

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

因为存在拼写错误(&#34; kingdom.buildings。$&#34;应该是&#34;王国 s .buildings。$&#34;)。 /> 但是,这种方式无法实现您的期望 $ 总是针对 kingdoms.buildings - 第一个数组的王国

这是一种应该能够解决问题的方法 (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();