Mongo Query仅返回SubDocuments的子集

时间:2015-11-15 08:28:18

标签: mongodb mongodb-query aggregation-framework

使用Mongo文档中的示例:

{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }

db.survey.find(
   { id: 12345, results: { $elemMatch: { product: "xyz", score: { $gte: 6 } } } }
)

如何返回调查12345(无论是否进行调查),但返回分数大于6的调查?换句话说,我不希望文档被取消基于子文档的结果,我想要文档但只是子文档的子集。

3 个答案:

答案 0 :(得分:3)

您要求的不是“查询”,而是基本上只是对每个文档中数组内容的过滤。

您使用.aggregate()$project执行此操作:

db.survey.aggregate([
    { "$project": {
        "results": {
            "$setDifference": [
                { "$map": {
                    "input": "$results",
                    "as": "el",
                    "in": {
                        "$cond": [
                            { "$and": [
                                { "$eq": [ "$$el.product", "xyz" ] },
                                { "$gte": [ "$$el.score", 6 ] }
                            ]}
                        ]
                    }
                }},
                [false]
            ]
        }
    }}
])

因此,对于具有与条件匹配的数组成员的文档而不是“禁止”结果,所有这一切都是“过滤”与条件不匹配的数组成员,但如果需要则返回带有空数组的文档是

目前最快的方法是$map检查所有元素,$setDifference过滤掉该检查返回的false值。可能的缺点是“set”必须包含唯一元素,所以只要元素本身是唯一的就可以了。

将来的版本将采用$filter方法,类似于$map的结构,但直接删除不匹配的结果,而$map只返回它们(通过{{1并且匹配元素或$cond)然后更适合。

否则,如果不是唯一的或者MongoDB服务器版本小于2.6,则使用false以非高效的方式执行此操作:

$unwind

在设计和性能方面都非常糟糕。因此,您可能最好在客户端代码中对每个文档进行过滤。

答案 1 :(得分:2)

您可以在mongoDB 3.2中使用$ filter

db.survey.aggregate([{
        $match: {
             { id: 12345}
        }
    }, {
        $project: {
            results: {
                $filter: {
                    input: "$results",
                    as: "results",
                    cond:{$gt: ['$$results.score', 6]}
                }
            }
        }
    }]);

它将返回分数大于6的所有子文档。如果您只想返回第一个匹配的文档,那么您可以使用' $'操作

答案 2 :(得分:1)

您可以这样使用$ redact:



db.survey.aggregate( [ 
  { $match : { _id : 12345 }},
  { $redact: {
     $cond: {
        if: { 
          $or: [ 
            { $eq: [ "$_id", 12345 ] },
            { $and: [ 
              { $eq: [ "$product", "xyz" ] }, 
              { $gte: [ "$score", 6 ] }
            ]}
          ] 
        },
        then: "$$DESCEND",
        else: "$$PRUNE"
      }
    }
  }
] );




首先匹配_id:12345然后它将" $$ PRUNE"所有没有产品的子文档"产品":" xyz"并且没有得分大于或等于6.我添加条件($ cond){$ eq:[" $ _ id",12345]}以便它不会修剪整个文档到达子文档之前。