我有一个具有以下结构的集合:
{
field1:'Foo',
field2:11,
stats:{
A:10,
B:15,
C:10
}
}
现在,我想总结统计字段的所有属性。 但是stats字段是Object,并且文档之间可能会有所不同(总是只有数字字段,但字段名称可以更改), 所以它看起来像这样:
{
field1:'Foo2',
field2:12,
stats:{
A:10,
B:10,
D:5
}
}
有没有一种方法可以使用Aggregation或$ sum获得如下结果:
{
sumStats:{
A:20,
B:25,
C:10,
D:5
}
}
答案 0 :(得分:1)
您可以尝试以下汇总:
db.col.aggregate([
{
$project: {
stats: { $objectToArray: "$stats" }
}
},
{
$unwind: "$stats"
},
{
$group: {
_id: "$stats.k",
value: { $sum: "$stats.v" }
}
},
{
$group: {
_id: null,
values: { $push: { k: "$_id", v: "$value" } }
}
},
{
$replaceRoot: {
newRoot: { $arrayToObject: "$values" }
}
}
])
由于密钥未知,您需要$objectToArray才能将stats
转换为键值对列表。然后,您可以使用$unwind为每个键值对获取单独的文档。在下一步中,您可以使用$group汇总所有文档中的值。结果,您需要一个对象,因此应按null
分组以将一个对象中的所有值累加为键值对列表,然后可以使用$arrayToObject和$replaceRoot来获得最终结构。
答案 1 :(得分:0)
您也可以尝试mapReduce聚合。文档说它可能效率不高,但是很容易阅读(这是使用Mongoose语法,但仅是Mongo驱动程序中同名函数的一个薄包装):
const mrd = {
//creates an output bucket for each property in stats object
map: function () { for (const prop in this.stats) emit(prop, this.stats[prop])},
//sums all the values in the output bucket for each property
reduce: function (k, vals) { return vals.reduce((accumulator, currentValue) => accumulator + currentValue, 0)}
};
YourModel.mapReduce(mrd, function (err, results) {
console.log(results)
//something like this (ran at Mongo console, assuming Mongoose output is similar):
//[{ "_id" : "a", "value" : 20 },
//{ "_id" : "b", "value" : 25 },
//{ "_id" : "c", "value" : 10 }
//{ "_id" : "d", "value" : 5 }
})
输出不在一个对象中,但这很容易以直接代码(例如Array.mapReduce())完成;
参考: http://mongoosejs.com/docs/api.html#mapreduce_mapReduce https://docs.mongodb.com/manual/tutorial/map-reduce-examples/