我正在尝试对某些集合(MongoDB 3.4)执行复杂的聚合查询。在这种聚合中,我需要消除重复项,为此,我希望使用$setUnion
运算符。
请考虑以下内容:
const a = ObjectId();
const b = ObjectId();
const c = ObjectId();
const d = ObjectId();
db.getCollection('test').insertMany([
{
arr: [a, b]
},
{
arr: [a, b, c]
},
{
arr: [d]
},
{
arr: [a, d]
}
])
现在我执行此聚合:
db.getCollection('test').aggregate([
{
$group: {
_id: null,
dups: {$push: '$arr'}
}
},
{
$project: {
_id: 0,
noDupsDoesNotWork: {
$setUnion: '$dups'
}
}
}
])
并收到:
{
"noDupsDoesNotWork" : [
[
ObjectId("5b839b38f6db291c78201c0d"),
ObjectId("5b839b38f6db291c78201c0e")
],
[
ObjectId("5b839b38f6db291c78201c0d"),
ObjectId("5b839b38f6db291c78201c0e"),
ObjectId("5b839b38f6db291c78201c0f")
],
[
ObjectId("5b839b38f6db291c78201c0d"),
ObjectId("5b839b38f6db291c78201c10")
],
[
ObjectId("5b839b38f6db291c78201c10")
]
]
}
这是我不了解的,因为据说$setUnion
可用于数组数组。在我的示例中,我期望它对4个数组执行集合并集。
但是,如果执行此聚合查询:
db.getCollection('test').aggregate([
{
$group: {
_id: null,
dups: {$push: '$arr'}
}
},
{
$project: {
_id: 0,
flattened: {
$reduce: {
input: '$dups',
initialValue: [],
in: {$concatArrays: ['$$value', '$$this']}
}
}
}
},
{
$project: {
noDups: {$setUnion: '$flattened'}
}
}
])
然后我会得到预期的结果:
{
"noDups" : [
ObjectId("5b839b38f6db291c78201c0d"),
ObjectId("5b839b38f6db291c78201c0e"),
ObjectId("5b839b38f6db291c78201c0f"),
ObjectId("5b839b38f6db291c78201c10")
]
}
但是这次$setUnion
适用于单个对象数组,而不是数组数组。
我更加困惑,因为如果我明确传递四个子数组,我将获得预期的结果:
db.getCollection('test').aggregate([
{
$group: {
_id: null,
dups: {$push: '$arr'}
}
},
{
$project: {
_id: 0,
explicit: {
$setUnion: [
{$arrayElemAt: ['$dups', 0]},
{$arrayElemAt: ['$dups', 1]},
{$arrayElemAt: ['$dups', 2]},
{$arrayElemAt: ['$dups', 3]},
]
}
}
}
])
我在这里想念什么?
为清楚起见,这是所需的输出(上面已详细说明,但再次添加了):
{
"noDups" : [
ObjectId("5b839b38f6db291c78201c0d"),
ObjectId("5b839b38f6db291c78201c0e"),
ObjectId("5b839b38f6db291c78201c0f"),
ObjectId("5b839b38f6db291c78201c10")
]
}
答案 0 :(得分:0)
为什么会这样? 好吧,不管你信不信,这实际上是 Mongo 的一个“特性”。 您可以跟踪一个打开的 feature change request。但让我们了解一下why:
<块引用>有一类表达式可以接受多个参数,表示为数组。 (在代码中,这些继承自 ExpressionNary。)对于任何这样的 n-ary 表达式,我将称之为 $nary,语法 {$nary: "$myArg"} 只是 {$nary: ["$myArg "]}。这两个表达的意思相同。
所以这一类独特的表达式包括许多运算符,其中之一是 $setUnion。 Mongo 的表达式语言只是将这些运算符的任何 '$arg' 解析为 ['$arg']。
这就是为什么在您使用 $dups
表达式时会发生“意外”行为,而在您明确写入相同值时不会发生的原因。
对于没有阅读您的完整问题的人,我将再次重复这个(最好的)词,您想在使用 $setUnion 展平数组后使用 $reduce 像这样:
{
$addFields: {
flat: {
$setUnion: {
$reduce: {
input: '$myArray',
initialValue: [],
in: {$concatArrays: ['$$this', '$$value']}
}
}
}
}
}