【问题标题】:MongoDB sum arrays from multiple documents on a per-element basisMongoDB 基于每个元素对来自多个文档的数组求和
【发布时间】:2016-02-18 11:00:00
【问题描述】:

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

{
  _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),但我无法完全到达那里。

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

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

这给了我

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

但我不知道如何获得每个元素的总和

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    如果您有 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}
          }
        ]
    )
    

    【讨论】:

    • 作品之类的。但它不是创建单个数组,而是简单地一一列出总和,例如。 "NumberLong(3), "结果:15"
    • 我已经更新了投影的答案,你能再测试一次吗?
    • SyntaxError: invalid property id @(shell):11:2
    • 好的,我修正了语法你可以再试一次吗?
    • 我们应该在第二个分组之前添加排序。我已添加回答。
    【解决方案2】:

    对此有另一种方法,但考虑到不同的方法将涉及使用$push 创建一个“数组数组”,然后应用 MongoDB 3.4 中引入的$reduce,里程可能会有所不同。将$sum 那些数组元素合并为一个数组结果:

    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" }
              }
            }
          }
        }
      }}
    ])
    

    真正的技巧是在"input"$map 中,我们使用$zip 操作为两个数组输入“成对”创建一个转置的数组列表。

    在第一次迭代中,它采用提供给$reduce 的空数组,并考虑到第一个对象,返回“压缩”输出,如下所示:

    [ [0,1], [0,3], [0,5], [0,7], [0,9] ]
    

    因此,useLongestLength 会将空数组替换为 0 值到当前数组的长度,然后将它们“压缩”在一起,如上所示。

    使用$map 处理,每个元素都受$sum 的约束,它将返回的结果“减少”为:

    [ 1, 3, 5, 7, 9 ]
    

    在第二次迭代中,“array of arrays”中的下一个条目将被$zip 拾取和处理,以及之前的“reduced”内容为:

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

    然后受制于$map的每个元素,再次使用$sum来产生:

    [ 3, 7, 11, 15, 19 ]
    

    由于只有两个数组被推入“数组数组”,即操作结束,也是最终结果。但否则$reduce 将继续迭代,直到处理完输入的所有数组元素。


    因此,在某些情况下,这将是性能更高的选项,也是您应该使用的选项。但请注意,特别是当使用 null$group 时,您要求将“每个”文档将 $push 内容放入数组以获得结果。

    这可能是在极端情况下打破 BSON 限制的一个原因,因此在将位置数组内容聚合到大型结果时,最好将$unwindincludeArrayIndex 选项一起使用。

    或者实际上好好看看这个过程,特别是如果有问题的“位置数组”实际上是其他一些“聚合操作”的结果,那么你应该看看之前的管道阶段用于创建“位置数组”。然后考虑,如果您希望将这些位置“进一步汇总”到新的总数中,那么您实际上应该在获得位置结果“之前”这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-28
      • 1970-01-01
      • 2017-12-23
      • 1970-01-01
      相关资源
      最近更新 更多