我有一个像这样的数组国家/地区的集合。我想总结各国的价值观。
{
"_id": ObjectId("54cd5e7804f3b06c3c247428"),
"country_json": {
"AE": NumberLong("13"),
"RU": NumberLong("16"),
"BA": NumberLong("10"),
...
}
},
{
"_id": ObjectId("54cd5e7804f3b06c3c247429"),
"country_json": {
"RU": NumberLong("12"),
"ES": NumberLong("28"),
"DE": NumberLong("16"),
"AU": NumberLong("44"),
...
}
}
如何汇总各国的价值以获得这样的结果?
{
"AE": 13,
"RU": 28,
..
}
答案 0 :(得分:2)
这可以使用aggregation
> db.test.aggregate([
{$project: {
RU: "$country_json.RU",
AE: "$country_json.AE",
BA: "$country_json.BA"
}},
{$group: {
_id: null,
RU: {$sum: "$RU"},
AE: {$sum: "$AE"},
BA: {$sum: "$BA"}
}
])
输出:
{
"_id" : null,
"RU" : NumberLong(28),
"AE" : NumberLong(13),
"BA" : NumberLong(10)
}
答案 1 :(得分:1)
如果您打算在"键"之间汇总统计数据,那么这不是一个非常好的文档结构。像这样。并非真正喜欢"数据作为关键名称"无论如何,但重点是它没有“好好发挥”#34;由于各地的密钥名称不同,因此有许多MongoDB查询表单。
特别是对于聚合框架,存储数据的更好形式是在实际数组中,如下所示:
{
"_id": ObjectId("54cd5e7804f3b06c3c247428"),
"countries": [
{ "key": "AE", "value": NumberLong("13"),
{ "key": "RU", "value": NumberLong("16"),
{ "key": "BA", "value": NumberLong("10")
]
}
通过它,您可以简单地使用聚合操作:
db.collection.aggregate([
{ "$unwind": "$countries" },
{ "$group": {
"_id": "$countries.key",
"value": { "$sum": "$countries.value" }
}}
])
这会给你带来如下结果:
{ "_id": "AE", "value": NumberLong(13) },
{ "_id": "RU", "value": NumberLong(28) }
这种结构确实发挥得很好并且#34;使用聚合框架和其他MongoDB查询模式,因为它实际上是它的预期和#34;要以这种方式使用数据时要完成。
在不更改文档结构的情况下,您必须使用JavaScript评估方法来遍历文档的键,因为这是使用MongoDB执行此操作的唯一方法:
db.collection.mapReduce(
function() {
var country = this.country_json;
Object.keys(country).forEach(function(key) {
emit( key, country[key] );
});
},
function(key,values) {
return values.reduce(function(p,v) { return NumberLong(p+v) });
},
{ "out": { "inline": 1 } }
)
这将产生与聚合示例输出中显示的完全相同的结果,但是使用当前文档结构。当然,JavaScript评估的使用效率不如聚合框架使用的本机方法那么高,因此它不会表现得那么好。
还要注意这里可能遇到的问题"大值"在您的转换NumberLong
字段中,因为它们以JavaScipt的方式表示的主要原因是JavaScipt本身对该值的大小有限制,而不是可以表示的。可能你的价值观是微不足道的,但只是简单地“#34; cast"那样,但是根据意图,对于足够大的数字,那么数学就会失败。
因此,考虑更改构建此数据的方式以简化操作通常是个好主意。最后要注意的是,您对单个文档中所有键所期望的输出类似于反直觉,因为它还需要遍历"散列/映射"而不是使用数组或游标的自然迭代器。