阵列过滤只能由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,它具有对基本投影可以执行的文档的“加强”操纵。
谢谢@尼尔,这样一个辉煌的解释。 – FRizal 2014-11-04 02:10:02