MongoDB基于每个元素对来自多个文档的数组进行求和

时间:2016-02-18 11:00:00

标签: mongodb mongodb-query aggregation-framework

我有以下文档结构(本例简化)

{
  _id : ObjectId("sdfsdf"),
  result : [1, 3, 5, 7, 9]
},
{
  _id : ObjectId("asdref"),
  result : [2, 4, 6, 8, 10]
}

我想得到那些result数组的总和,但不是总和,而是一个新元素,对应于元素基础上原始数组的总和,即

result : [3, 7, 11, 15, 19]

我在这里搜索过无数问题,其中一些问题已接近尾声(例如this onethis onethis one),但我无法到达那里。< / p>

我可以得到每个数组的总和

aggregate(
    [
      {
        "$unwind" : "$result"
      },
      {
        "$group": {
          "_id": "$_id",
          "results" : { "$sum" : "$result"}
          }
      }
    ]
)

给了我

[ { _id: sdfsdf, results: 25 },
  { _id: asdref, results: 30 } ]

但我无法弄清楚如何获得每个元素的总和

2 个答案:

答案 0 :(得分:5)

如果你有3.2或更新的MongoDb,你可以使用includeArrayIndex

然后你应该改变$unwind

您的代码应该是这样的:

.aggregate(
    [
      {
        "$unwind" :  { path: "$result", includeArrayIndex: "arrayIndex" }
      },
      {
        "$group": {
          "_id": "$arrayIndex",
          "results" : { "$sum" : "$result"}
          }
      },
      { 
        $sort: { "_id": 1}
      },
      {
        "$group":{
          "_id": null,
          "results":{"$push":"$results"}
          } 
      },
      {
        "$project": {"_id":0,"results":1}
      }
    ]
)

答案 1 :(得分:1)

对此有一种替代方法,但考虑到使用$push来创建数组数组&#34;然后将MongoDB 3.4中引入的$reduce应用于$sum这些数组元素到单个数组结果中:

   you may try this:

   CREATE TABLE #STATUS 
    (
        [STATUS] INT,
        [START] DATE,
        [END] DATE
    )

    INSERT INTO #STATUS
     (
      [STATUS], [START], [END]
     )
    VALUES
        (32,    '20170101', '20170201'),
        (32,    '20170201', '20170204'),
        (1,     '20170204', '20170306'),
        (1,     '20170306', '20170509'),
        (32,    '20170509', '20170519'),
        (32,    '20170519', '20170622')

    SELECT 
      A.STATUS
     ,A.[START]
     ,B.[END] 
    FROM #STATUS A 
    LEFT JOIN #STATUS B 
    ON A.[STATUS]=B.[STATUS]
    AND A.[END]=B.[START]
    WHERE B.STATUS IS NOT NULL

db.collection.aggregate([ { "$group": { "_id": null, "result": { "$push": "$result" } }}, { "$addFields": { "result": { "$reduce": { "input": "$result", "initialValue": [], "in": { "$map": { "input": { "$zip": { "inputs": [ "$$this", "$$value" ], "useLongestLength": true } }, "as": "el", "in": { "$sum": "$$el" } } } } } }} ]) $map中的真正技巧我们使用$zip操作创建了一个转置的数组列表&#34;成对&#34;对于两个数组输入。

在第一次迭代中,这将获取提供给$reduce的空数组,并返回&#34;压缩&#34;输出考虑到第一个对象:

"input"

因此[ [0,1], [0,3], [0,5], [0,7], [0,9] ] 会将空数组的useLongestLength值替换为当前数组的长度,并且&#34; zip&#34;如上所述。

使用$map处理时,每个元素都受$sum的约束,其中&#34;减少&#34;返回的结果为:

0

在第二次迭代中,&#34;数组数组中的下一个条目&#34;将由$zip与之前的&#34;减少&#34;一起提取并处理。内容为:

[ 1, 3, 5, 7, 9 ]

然后再使用$map为每个元素$sum生成:

[ [1,2], [3,4], [5,6], [7,8], [9,10] ]

并且因为只有两个数组被推入&#34;阵列数组&#34;这是操作的结束,也是最终的结果。但是否则$reduce会继续迭代,直到输入的所有数组元素都被处理完毕。

所以在某些情况下,这将是更高效的选项以及您应该使用的内容。但需要注意的是,特别是在使用[ 3, 7, 11, 15, 19 ] $group时,您要问的是每个&#34;将$push内容记录到数组中以获得结果。

这可能是在极端情况下破坏BSON限制的原因,因此在将位置数组内容聚合到大结果上时,最好将$unwindnull选项一起使用。< / p>

或者实际上实际上要仔细研究这个过程,特别是如果&#34;位置数组&#34;问题实际上是其他一些&#34;聚合操作&#34;的结果,那么您应该查看用于创建&#34;位置数组&#34;的先前管道阶段。然后考虑一下,如果你想要那些职位&#34;进一步汇总&#34;对于新的总数,那么你实际上应该这样做&#34;之前&#34;获得了位置结果。