我有一个包含多个数组的文档的集合。这些通常都非常大,但为了解释,您可以考虑以下两个文档:
{
"obj1": [
{ "a": "a", "b": "b" },
{ "a": "a", "b": "c" },
{ "a": "a", "b": "b" }
],
"obj2": [
{ "a": "a", "b": "b" },
{ "a": "a", "b": "c" }
]
},
{
"obj1": [
{ "a": "c", "b": "b" }
],
"obj2": [
{ "a": "c", "b": "c" }
]
}
我们的想法是将数组中的匹配元素放到查询中。在多个数组中需要多个匹配,因此这不在投影和positional $
运算符的范围内。期望的结果如下:
{
"obj1": [
{ "a": "a", "b": "b" },
{ "a": "a", "b": "b" }
],
"obj2": [
{ "a": "a", "b": "b" },
]
},
传统的方法是这样的:
db.objects.aggregate([
{ "$match": {
"obj1": {
"$elemMatch": { "a": "a", "b": "b" }
},
"obj2.b": "b"
}},
{ "$unwind": "$obj1" },
{ "$match": {
"obj1.a": "a",
"obj1.b": "b"
}},
{ "$unwind": "$obj2" },
{ "$match": { "obj2.b": "b" }},
{ "$group": {
"_id": "$_id",
"obj1": { "$addToSet": "$obj1" },
"obj2": { "$addToSet": "$obj2" }
}}
])
但是在这两个数组中使用$unwind
会导致整个集合使用大量内存并减慢速度。 $addToSet
也可能存在问题,并且为每个数组拆分$group
阶段会使事情变得更慢。
所以我正在寻找一个不那么密集但过程相同的过程。
答案 0 :(得分:3)
由于MongoDB 3.0我们有$filter
运算符,这使得这非常简单:
db.objects.aggregate([
{ "$match": {
"obj1": {
"$elemMatch": { "a": "a", "b": "b" }
},
"obj2.b": "b"
}},
{ "$project": {
"obj1": {
"$filter": {
"input": "$obj1",
"as": "el",
"cond": {
"$and": [
{ "$eq": [ "$$el.a", "a" ] },
{ "$eq": [ "$$el.b", "b" ] }
]
}
}
},
"obj2": {
"$filter": {
"input": "$obj2",
"as": "el",
"cond": { "$eq": [ "$$el.b", "b" ] }
}
}
}}
])
MongoDB 2.6引入了$map
运算符,该运算符可以在不需要$unwind
的情况下对阵列进行操作。结合已添加到聚合框架的其他逻辑运算符和其他集合运算符,可以解决此问题和其他问题。
db.objects.aggregate([
{ "$match": {
"obj1": {
"$elemMatch": { "a": "a", "b": "b" }
},
"obj2.b": "b"
}},
{ "$project": {
"obj1": {
"$setDifference": [
{ "$map": {
"input": "$obj1",
"as": "el",
"in": {
"$cond": [
{ "$and": [
{ "$eq": [ "$$el.a", "a" ] },
{ "$eq": [ "$$el.b", "b" ] }
]},
"$$el",
false
]
}
}},
[false]
]
},
"obj2": {
"$setDifference": [
{ "$map": {
"input": "$obj2",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.b", "b" ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
])
它的核心在$map
运算符中,它通过允许处理所有数组元素而像内化$unwind
一样工作,但也允许操作在同一语句中对这些数组元素进行操作。通常情况下,这将在几个管道阶段完成,但我们可以在一个$project
,$group
或$redact
阶段进行处理。
在这种情况下,内部处理使用$cond
运算符,该运算符结合逻辑条件,以便为true
或false
返回不同的结果。在这里,我们使用$eq
运算符来测试当前元素中包含的字段的值,其方式与使用单独的$match
管道阶段的方式非常相似。 $and
条件是另一个逻辑运算符,它用于组合元素上多个条件的结果,这与$elemMatch
运算符在$match
管道阶段中工作的方式非常相似。 / p>
最后,由于我们的$cond
运算符用于返回当前元素的值,或false
如果条件不是true
,我们需要“过滤”任何{{1}来自数组的值产生了$map
操作。这是$setDifference
运算符用于比较两个输入数组并返回差异的位置。因此,当与仅包含false
元素的数组进行比较时,结果将是从$map
返回的元素,而$cond
元素不会来自$unwind
当条件不满足时。