This is very achievable with the aggregate, although a bit dumb, but allows you to run it:
db.collection.aggregate([
// First Group to get the *master* total for the documents
{"$group": {
"_id": "$subid",
"total": { "$sum": "$total" },
details: { "$push": "$details" }
}},
// Unwind the details
{"$unwind": "$details"},
// Unwind the details "again" since you *pushed* and array onto an array
{"$unwind":"$details"},
// Now sum up the values by each name (keeping levels)
{"$group": {
"_id:" {
"_id": "$_id",
"total": "$total",
"name": "$details.name"
},
"value": {"$sum": "$details.value"}
}},
// Sort the names (because you expect that!)
{"$sort": { "_id.name": 1}},
// Do some initial re-shaping for convenience
{"$project": {
"_id": "$_id._id",
"total": "$_id.total",
"details": { "name": "$_id.name", "value": "$value" }
}},
// Now push everything back into an array form
{"$group": {
"_id": {
"_id": "$_id",
"total": "$total"
},
"details": {"$push": "$details"}
}},
// And finally project nicely
{"$project": {
"_id": "$_id._id",
"total": "$_id.total",
"details": 1
}}
])
So, if you tried earlier, you could skip the concept of creating an initial group to get the sum of the top level in the field totalin your documents.
, - "" , . , , , "-" .
, $group name:
equiv (GROUP BY _id, total, "details.name" )
, . name ( ), , , $project .
, , . , double unwind.