在MongoDB中聚合可选数组中的数据

时间:2014-08-31 16:59:02

标签: mongodb mongodb-query aggregation-framework

使用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 

2 个答案:

答案 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 }