【问题标题】:How to aggregate array value objects by key in MongoDB如何在 MongoDB 中按键聚合数组值对象
【发布时间】:2021-02-10 04:24:43
【问题描述】:

给定以下集合

{ "_id": 1, "items": [ { "k": "A", "v": 1 }, { "k": "B", "v": 2 } ] }
{ "_id": 2, "items": [ { "k": "A", "v": 3 }, { "k": "B", "v": 4 } ] }

如何对具有相同键 k 的所有项目求和,并保留原始对象格式,如下所示:

{ "items": [ { "k": "A", "v": 4 }, { "k": "B", "v": 6 } ] }

我尝试使用$unwind$group,但它返回的是对象序列而不是单个项目。

{ $unwind: { path: "$items" } },
{
  $group: {
    _id: "$items.k",
    v: { $sum: "$items.v" }
  }
}

我可以将其聚合回原始格式,但我觉得必须有更好的方法来做到这一点。

【问题讨论】:

    标签: mongodb aggregation-framework nosql-aggregation


    【解决方案1】:

    您可以使用自定义 $accumulator 以您想要的方式合并对象:

    db.collection.aggregate([
        {$project: {
                items: {
                    $arrayToObject: "$items"
                }
        }},
        {$group: {
                _id: null,
                items: {
                    $accumulator: {
                        init: function(){ return {}; },
                        accumulate: function(obj, doc){
                            Object.keys(doc).forEach(function(k){
                                obj[k] = (obj[k]?obj[k]:0) + doc[k];
                            })
                            return obj;
                        },
                        accumulateArgs: ["$items"],
                        merge: function(obj, doc){
                            Object.keys(doc).forEach(function(k){
                                obj[k] = (obj[k]?obj[k]:0) + doc[k];
                            })
                            return obj;
                        }
                    }
                }
        }},
        {$project:{
            _id:0,
            items:{$objectToArray:"$items"}
        }}
    ])
    

    【讨论】:

    • 感谢乔的回答!我无法在 Mongodb playground 中运行此代码。通过阅读代码,我了解到键在所有文档中的顺序和位置都应该相同。我的理解正确吗?
    • 否,但它确实假定所有键都是数字的。键将按名称匹配并求和。
    • 谢谢乔。尽管此解决方案回答了我的问题,但$accomulator 的文档建议尽可能使用管道运算符。 “在聚合运算符中执行 JavaScript 可能会降低性能。仅当提供的管道运算符无法满足应用程序的需求时,才使用 $accumulator 运算符。”
    【解决方案2】:
    • $unwind解构items数组
    • $groupitems.k 得到 v 的总和
    • $group by null 并重构 items 数组
    db.collection.aggregate([
      { $unwind: "$items" },
      {
        $group: {
          _id: "$items.k",
          v: { $sum: "$items.v" }
        }
      },
      {
        $group: {
          _id: null,
          items: {
            $push: {
              k: "$_id",
              v: "$v"
            }
          }
        }
      }
    ])
    

    Playground

    【讨论】:

    • 谢谢您。这也回答了我的问题。这是最优雅的解决方案,尽管我选择了另一个答案,因为它在聚合中提供了更大的灵活性。
    • 编辑:我将此答案标记为正确,因为它是根据 MongoDB 文档推荐的解决方案。
    猜你喜欢
    • 2016-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-02
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-20
    相关资源
    最近更新 更多