使用MongoDB聚合管道,有可能对可选数组字段中的值求和吗?
假设这个集合:
db.myCollection.insert([
{_id: 1},
{ _id: 2,
events: [{_id: 201, value: 10}, {_id:202, value:20}]
}])
我想使用聚合管道对events.value字段求和,以产生这个:
{_id: 1, totalValue: 0},
{_id: 2, totalValue: 30}
我无法使用{$unwind: "$events"}
,因为这会从输出中消除{_id: 1}
,因此我尝试将值推送到数组,并使用$cond
创建单个如果元素丢失,则[0]
:
db.myCollection.aggregate([
{$group: {_id:"$_id", values: {$push: "$events.value"}}},
{$project: {_id:1, values: {$cond: {
if: {$gt: [{$size: "$values"}, 0]},
then: "$values",
else: [[0]]
}}}} ])
这会创建以下输出:
{ "_id" : 2, "values" : [ [ 10, 20 ] ] }
{ "_id" : 1, "values" : [ [ 0 ] ] }
现在我可以对值使用$unwind
,但我无法对值进行求和。
将$group
与$sum
db.myCollection.aggregate([
{$group: {_id:"$_id", values: {$push: "$events.value"}}},
{$project: {_id:1, values: {$cond: {
if: {$gt: [{$size: "$values"}, 0]},
then: "$values",
else: [[0]]
}}}},
{$unwind: "$values"},
{$group: {_id:"$_id", totalValue: {$sum: "$values"}}} ])
产生这个:
{ "_id" : 1, "totalValue" : 0 }
{ "_id" : 2, "totalValue" : 0 }
并将$project
与$add
一起使用会产生错误:
db.myCollection.aggregate([
{$group: {_id:"$_id", values: {$push: "$events.value"}}},
{$project: {_id:1, values: {$cond: {
if: {$gt: [{$size: "$values"}, 0]},
then: "$values",
else: [[0]]
}}}},
{$project: {_id:1, totalValue: {$add: "$values"}}} ])
导致异常:
exception: $add only supports numeric or date types, not Array
答案 0 :(得分:3)
因此,您的文档可能包含也可能不包含您希望求和的数组元素。因此,在执行任何$unwind
操作之前,请将$ifNull
运算符应用于$project
。这会将字段作为参数进行测试,如果字段不存在或者求值为null
则返回备用值,否则返回存在的字段的值:
db.myCollection.aggregate([
{ "$project": {
"events": { "$ifNull": [ "$events", [{ "value": 0 }] ] }
}},
{ "$unwind": "$events" },
{ "$group": {
"_id": "$_id",
"totalValue": { "$sum": "$events.value" }
}}
])
一种非常简化的形式,因为内容既没有因为缺少数组而被删除,而是被替换为默认值,而这个默认值可以用于您的意图。
答案 1 :(得分:0)
我错过了$unwind
声明:
一个$unwind
之后:
db.myCollection.aggregate([
{$group: {_id:"$_id", values: {$push: "$events.value"}}},
{$project: {_id:1, values: {$cond: {
if: {$gt: [{$size: "$values"}, 0]},
then: "$values",
else: [[0]]
}}}},
{$unwind: "$values"} ])
我明白了:
{ "_id" : 2, "values" : [ 10, 20 ] }
{ "_id" : 1, "values" : [ 0 ] }
但如果我添加第二个$unwind
:
db.myCollection.aggregate([
{$group: {_id:"$_id", values: {$push: "$events.value"}}},
{$project: {_id:1, values: {$cond: {
if: {$gt: [{$size: "$values"}, 0]},
then: "$values",
else: [[0]]
}}}},
{$unwind: "$values"},
{$unwind: "$values"}])
我明白了:
{ "_id" : 2, "values" : 10 }
{ "_id" : 2, "values" : 20 }
{ "_id" : 1, "values" : 0 }
现在我可以添加$group
子句:
db.myCollection.aggregate([
{$group: {_id:"$_id", values: {$push: "$events.value"}}},
{$project: {_id:1, values: {$cond: {
if: {$gt: [{$size: "$values"}, 0]},
then: "$values",
else: [[0]]
}}}},
{$unwind: "$values"},
{$unwind: "$values"},
{$group: {_id:"$_id", totalValue: {$sum: "$values"}}}])
产生所需的结果:
{ "_id" : 1, "totalValue" : 0 }
{ "_id" : 2, "totalValue" : 30 }