2014-11-03 52 views
0

如果我想通过指定一个电子邮件ID来查询员工的属性,我确实有这个工作。在结果中查询过滤数组中的多个元素

db.employee.find({},{ 
       _id: 0, 
       employee: { 
        $elemMatch: { 
         email: "[email protected]" 
        } 
       }}) 

假设我想通过指定多个电子邮件ID来查询多个员工的属性。 我读通过,它是与$或操作员,但我不知道如何打好,一出..

我MongoDB的数据按照下面的例子:

{ 
"_id" : ObjectId("53dbb05fa976627439d43884"), 
"employee" : [ 
    { 
    "email" : "[email protected]", 
    "deptName" : "x", 

}, 
{ 
    "email" : "[email protected]", 
    "deptName" : "y", 

}, 
{ 
    "email" : "[email protected]", 
    "deptName" : "y", 

} 
] 
} 

回答

1

阵列过滤只能由aggregation framework完成。它允许对文档进行更多的操作,而不是基本投影。

就像任何查询一样,你应该总是先使用$match管道,以便在可能的情况下使用索引。不管之后正在执行什么其他操作:

db.employee.aggregate([ 

    // Always match first to reduce results 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Unwind to de-normalize the array elements as documents 
    { "$unwind": "$employee" }, 

    // Match to "filter" the array content 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Group back to a document with the array 
    { "$group": { 
     "_id": "$_id", 
     "employee": { "$push": "$employee" } 
    }}, 

    // Optionally project to remove the "_id" field from results 
    { "$project": { 
     "_id": 0, 
     "employee": 1 
    }} 
]) 

这样就解释了基本过程。在找到符合条件的“文档”之后,您可以使用$unwind有效地将数组的每个元素独立为文档,共享任何父字段。额外的$match用于“过滤”结果中的那些元素。当$group完成时,只有匹配的元素被放回到数组中。

对于MongoDB 2.6,你可以用不同的方式来做到这一点,这应该适用于更大的数组。有一些新的运算符,如$map用于在不使用$unwind的情况下处理“内联”阵列。还有其他“设置”过滤选项,如$setDifference。所以,你可以做到这一点您的文档始终包含在各自为阵独特的“电子邮件”值:

db.employee.aggregate([ 

    // Always match first to reduce results 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Project filtered array content "in-line" 
    { "$project": { 
     "_id": 0, 
     "employee": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$employee", 
        "as": "el", 
        "in": { 
         "$cond": [ 
          { "$or": [ 
           { "$eq": [ "$$el.email", "[email protected]" ] }, 
           { "$eq": [ "$$el.email", "[email protected]" ] } 
          ]}, 
          "$$el", 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }} 
]) 
从前面提到的 $cond操作者用在这里是数组中的每个元素评估新运营商

除了通过$map看看是否符合条件。如果是这样,则元素返回结果数组中,否则元素为false

$setDifference运算符然后“过滤”返回的“set”中的任何false值,就像任何重复数据一样,所以数组元素在每个文档中需要如前所述是唯一的。

对于“非唯一”的元素,总有这样的替代品在现代版本的第一方法,以及:

db.employee.aggregate([ 

    // Always match first to reduce results 
    { "$match": { 
     "employee.email": { "$in": ["[email protected]", "[email protected]"] } 
    }}, 

    // Redact removes document levels that do not match the condition 
    { "$redact": { 
     "$cond": [ 
      { "$or": [ 
       { "$eq": [ 
        { "$ifNull": [ "$email", "[email protected]" ] }, 
        "[email protected]" 
       ]}, 
       { "$eq": [ 
        { "$ifNull": [ "$email", "[email protected]" ] }, 
        "[email protected]" 
       ]} 
      ]}, 
      "$$DESCEND", 
      "$$PRUNE" 
     ] 
    }} 
]) 

这使用$redact以略微做作方法,以消除从文档的数组元素那与条件不符。这里的问题是$redact是递归的,所以我们测试被测场的存在以及它不存在的地方只是返回一个值来匹配。实际上真的只需要一个$ifNull声明。

本质上,无论您选择什么方法,它都是aggregation framework,它具有对基本投影可以执行的文档的“加强”操纵。

+0

谢谢@尼尔,这样一个辉煌的解释。 – FRizal 2014-11-04 02:10:02

0

如果我有明白你的意思,我认为你的解决方案是$in运营商,我认为如果我了解你的需求,你应该把你的条件放在选择器参数中,而不是投影中。所以,你的查询将是这样的:

db.employee.find({email:{$in:["[email protected]", "[email protected]oo"]}},{_id:0}) 
+0

我已经在问题里面添加了mongodb数据......我想这也增加了复杂度 – FRizal 2014-11-03 15:33:57

+0

是的,我不明白你的数据是如何格式化的。所以你不能用投影做到这一点,因为$ elemMatch只返回数组中的一个元素。你需要一个更复杂的聚集 – mgaido 2014-11-03 15:38:14

0

投影算$ elemMatch只返回数组中的一个元素,所以我觉得你可以考虑,像

var emails = [ "[email protected]", "[email protected]" ]; 
var match = { 
    $match : { 
     "employee.email" : { 
      $in : emails 
     } 
    } 
}; 

db.employee.aggregate([ match, { 
    $unwind : "$employee" 
}, match, { 
    $group : { 
     _id : "$_id", 
     employee : { 
      $push : "$employee" 
     } 
    } 
}, { 
    $project : { 
     _id : 0, 
     employee : 1 
    } 
} ]); 
+0

得到这个未捕获的异常:总失败:{ \t “ERRMSG”: “异常:坏查询:BADVALUE未知名顶级运营商:$的”, \t “代码”:16810, \t“OK “:0 } – FRizal 2014-11-03 15:45:28

+0

@Fairul,对不起,** $ match **部分存在错误。固定。 – Wizard 2014-11-04 01:08:09

+0

感谢@Wizard,它的作品像魅力 – FRizal 2014-11-04 02:09:34