【问题标题】:Sum array number values across multiple documents跨多个文档求和数组数值
【发布时间】:2018-04-20 00:54:22
【问题描述】:

我有 Mongo 文档,其中包含按顺序排列的数组数字值(按天),我想对数组外按字段分组的每个位置的多个文档中的相同值求和。

{"_id" : "1",
 "group" : "A",
 "value_list" : [1,2,3,4,5,6,7]
},
{"_id" : "2",
 "group" : "B",
 "value_list" : [10,20,30,40,50,60,70]
},
{"_id" : "3",
 "group" : "A",
 "value_list" : [1,2,3,4,5,6,7]
},
{"_id" : "4",
 "group" : "B",
 "value_list" : [10,20,30,40,50,60,70]
}

所以下面列出了我所追求的结果。

上面有两个A组文档,在value_list数组的位置1,两个文档的值都是1。所以1+1=2。位置 2,两个文档中的值都是 2,所以 2+2=4,依此类推。

上面有两个 B 组文档,在 value_list 数组的位置 1,两个文档的值都是 10。所以 10+10=20。位置 2,两个文档中的值都是 20,所以 20+20=40,等等。

{"_id" : "30",
 "group" : "A",
 "value_list" : [2,4,6,8,10,12,14]
},
{"_id" : "30",
 "group" : "A",
 "value_list" : [20,40,60,80,100,120,140]
}

我将如何使用 Mongo 脚本执行此操作?谢谢,马特

【问题讨论】:

  • 我认为您的意思是“A”和“B”的组,但您只是复制了输出而没有调整值。
  • 大声笑,哎呀,是的,我的意思就是这样 - 下一个和 B 组的 id 40 :)
  • 是的,我也不认为你真的是指 id "30" 或 "40",除非有一些合乎逻辑的地方来自,而且它不在文档中。 MongoDB 无法输出“任意”键值。 “关键”输出本质上是您分组的内容。但我猜整个聚合概念对你来说是新事物。所以请继续阅读...
  • 也对@NeilLunn - 你在那里赚钱。我确实还有一个关于使用 $avg 而不是 $sum 的问题,并且还包括一个字段,其中包含平均使用的每组文档数的值。我也想用于基准测试,而且我通常只在一个组中至少有 10 个文档的情况下进行基准测试。谢谢

标签: mongodb aggregation-framework


【解决方案1】:

当然,最“可扩展”的方式是使用$unwindincludeArrayIndex 选项来跟踪位置,然后使用$sum“展开”组合,然后再添加回数组格式:

db.getCollection('test').aggregate([
  { "$unwind": { "path": "$value_list", "includeArrayIndex": "index" } },
  { "$group": {
    "_id": {
      "group": "$group",
      "index": "$index"
    },
    "value_list": { "$sum": "$value_list" }
  }},
  { "$sort": { "_id": 1 } },
  { "$group": {
      "_id": "$_id.group",
      "value_list": { "$push": "$value_list" }
  }},
  { "$sort": { "_id": 1 } }  
])

请注意,您需要在第一个 $group 之后添加 $sort 以保持数组位置。

如果你能侥幸成功,你也可以将所有数组应用到$reduce

db.getCollection('test').aggregate([
  { "$group": {
    "_id": "$group",
    "value_list": { "$push": "$value_list" }  
  }},
  { "$addFields": {
    "value_list": {
      "$reduce": {
        "input": "$value_list",
        "initialValue": [],
        "in": {
          "$map": {
            "input": {
              "$zip": {
                "inputs": ["$$this", "$$value"],
                "useLongestLength": true,
              }
            },
            "in": { "$sum": "$$this"}
          }
        }         
      } 
    }  
  }},
  { "$sort": { "_id": 1 } }
])

基本上,您使用初始$push 创建一个“数组数组”,然后使用$reduce 处理它。 $zip 对每个元素进行“成对”赋值,然后在 $map 期间使用 $sum 在每个位置将它们相加。

虽然效率更高一些,但对于大数据来说并不实用,因为在“减少”它之前,您可能会通过将所有分组的“数组”添加到分组中的单个数组中来打破 BSON 限制。

任何一种方法都会产生相同的结果:

/* 1 */
{
    "_id" : "A",
    "value_list" : [ 
        2.0, 
        4.0, 
        6.0, 
        8.0, 
        10.0, 
        12.0, 
        14.0
    ]
}

/* 2 */
{
    "_id" : "B",
    "value_list" : [ 
        20.0, 
        40.0, 
        60.0, 
        80.0, 
        100.0, 
        120.0, 
        140.0
    ]
}

【讨论】:

  • 哇,尼尔,太棒了!所以这种排序保留了数组位置,我不会想到这一点,事实上任何类型的排序都是我在这个用例中对数字值想到的最后一件事。感谢您在这里的帮助!
  • 如果我想要按组取平均值,我想最明显的方法是将 $sum 替换为 $avg?如果我想在输出中为每个组添加额外的文档数作为字段,您认为这很容易吗?
  • @MattLightbourn 是的。 $avg 也可以作用于值列表。 “计数”当然只是一个{ "$sum" : 1 },其中$group 语句只是在您的"group" 属性上“分组”。所以第一个列表中的最后一个$group 或第二个列表中的唯一一个。
猜你喜欢
  • 2016-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-26
  • 2023-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多