Given the following collection
{ "_id": 1, "items": [ { "k": "A", "v": 1 }, { "k": "B", "v": 2 } ] }
{ "_id": 2, "items": [ { "k": "A", "v": 3 }, { "k": "B", "v": 4 } ] }
How can I sum all the items having the same key k, preserving the original object format like so:
{ "items": [ { "k": "A", "v": 4 }, { "k": "B", "v": 6 } ] }
I tried using $unwind and $group, but it returns a sequence of objects instead of single item.
{ $unwind: { path: "$items" } },
{
$group: {
_id: "$items.k",
v: { $sum: "$items.v" }
}
}
I could aggregate it back into the original format, but I feel there must be a better way to do it.
You could use a custom $accumulator to merge the objects the way you want:
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"}
}}
])
$unwind deconstruct items array$group by items.k and get sum of v$group by null and reconstruct items arraydb.collection.aggregate([
{ $unwind: "$items" },
{
$group: {
_id: "$items.k",
v: { $sum: "$items.v" }
}
},
{
$group: {
_id: null,
items: {
$push: {
k: "$_id",
v: "$v"
}
}
}
}
])