Reputation: 118
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.
Upvotes: 2
Views: 3830
Reputation: 28316
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"}
}}
])
Upvotes: 1
Reputation: 36104
$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"
}
}
}
}
])
Upvotes: 0