如何在 MongoDB 中按键聚合数组值对象

How to aggregate array value objects by key in MongoDB

给出以下集合

{ "_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,但它 returns 对象序列而不是单个项目。

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

我可以把它聚合回原来的格式,但我觉得一定有更好的方法。

  • $unwind解构items数组
  • $group 通过 items.k 得到 v
  • 的总和
  • $group 为 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

您可以使用自定义 $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"}
    }}
])