2015-03-13 89 views
10

多个元素我有这样的MongoDB elemmatch在阵列

{ 
     "_id" : ObjectId("54e66b2da7b5f3a92e09dc6c"), 
     "SomeMetric" : [ 
      { 
       //some object 
      } 
      { 
       //some object 
      } 
     ], 
     "FilterMetric" : [ 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s1" 
      }, 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s2" 
      }, 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s1" 
      }, 
      { 
       "min" : "0.00", 
       "max" : "16.83", 
       "avg" : "0.00", 
       "class" : "s2" 
      } 
     ] 
    } 

一个mongodb的文档通常,它包含许多嵌套的数组这样。我想单独投影一个度量标准,只有具有我的搜索条件的数组。 我有查询

db.sample.find(
{"filtermetric.class" : "s2"},{"filtermetric" : { $elemMatch : {class: "s2"}}} 
) 

这给了我只有第一个对象在数组中。类别为s2的第二个对象不会返回。

,如果我尝试

db.sample.find(
    {"filtermetric" : { $elemMatch : {class: "s2"}}} 
    ) 

它给我所有的数组中的4个对象。

如何在这种情况下获得符合条件的所有对象?

回答

16

不能以任何形式的基本.find()查询返回与您的条件匹配的数组的多个元素。要匹配多个元素,您需要使用.aggregate()方法。

这里的主要区别在于“查询”完全符合它的意图,并匹配符合条件的“文档”。您可以尝试在投影参数中使用positional $运算符,但是其中的规则是它只会匹配与查询条件匹配的“第一个”数组元素。

为了“过滤器”的多个数组元素,步骤如下:

db.sample.aggregate([ 
    // Filter possible documents 
    { "$match": { "filtermetric.class": "s2" } }, 

    // Unwind the array to denormalize 
    { "$unwind": "$filtermetric" }, 

    // Match specific array elements 
    { "$match": { "filtermetric.class": "s2" } }, 

    // Group back to array form 
    { "$group": { 
     "_id": "$_id", 
     "filtermetric": { "$push": "$filtermetric" } 
    }} 
]) 

在是2.6或更高版本,你可以用$redact做到这一点的MongoDB的现代版本:

db.sample.aggregate([ 
    // Filter possible documents 
    { "$match": { "filtermetric.class": "s2" } }, 

    // Redact the entries that do not match 
    { "$redact": { 
     "$cond": [ 
      { "$eq": [ { "$ifNull": [ "$class", "s2" ] }, "s2" ] }, 
      "$$DESCEND", 
      "$$PRUNE" 
     ] 
    }} 
]) 

这可能是您最有效的选择,但它是递归的,所以首先考虑您的文档结构,因为在任何级别的任何其他条件下,相同的命名字段不能存在。

可能更安全,但只有在数组中的结果是“独一无二”有用的是这种技术与$map$setDifference

db.sample.aggregate([ 
    { "$project": { 
     "filtermetric": { "$setDifference": [ 
      { "$map": [ 
       "input": "$filtermetric", 
       "as": "el", 
       "in": {"$cond": [ 
        { "$eq": [ "$$el.class", "s2" ] }, 
        "$$el", 
        false 
       ]} 
      ]}, 
      [false] 
     ]} 
    }} 
]) 

还注意到,在这两个$group$project操作流水线阶段你需要指定您想要在该阶段的结果文档中返回的所有字段。

最后的注意事项是,当您只查询数组中单个键的值时,不需要$elemMatch"Dot notation"是首选,并且只在访问阵列中的单个密钥时推荐。 $elemMatch只应在数组“元素”内的文档中的“多个”键需要匹配查询条件时才需要。

+0

Thx很多。这是详细和解释性的。 – Manoj 2015-03-13 08:51:46

+0

在这些答案中'filtermetric'应该是'FilterMetric',以匹配示例文档中该字段的大小写。 – JohnnyHK 2015-06-03 03:38:25