我有以下几十个成就字段的用户记录集:
{
name: 'John',
achievements: {
champion: true,
commander: false
rockstar: true,
...
}
},
{
name: 'Lynda',
achievements: {
champion: false,
commander: true,
best_player: true,
...
},
},
...
}
我可以在没有map-reduce的情况下汇总每项成就的数量吗?
{
champion: 10,
commander: 5,
best_player: 2,
rockstar: 1,
smth_else: 0,
...
...
}
答案 0 :(得分:1)
如果您想使用聚合框架执行此操作,则至少需要MongoDB 3.4.4才能使用受支持的$arrayToObject
和$objectToArray
。
db.getCollection('people').aggregate([
{ "$addFields": {
"achievements": {
"$map": {
"input": { "$objectToArray": "$achievements" },
"in": {
"k": "$$this.k",
"v": { "$cond": ["$$this.v", 1, 0] }
}
}
}
}},
{ "$unwind": "$achievements" },
{ "$group": {
"_id": "$achievements.k",
"v": { "$sum": "$achievements.v" }
}},
{ "$group": {
"_id": null,
"data": { "$push": { "k": "$_id", "v": "$v" } }
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$data" }
}}
])
如果您没有该版本,那么只有方法是使用mapReduce。在介绍这些操作符之前,没有办法使用对象的键,而没有明确地声明所有这些操作符。您只能在JavaScript代码中执行相同类型的操作。这是mapReduce
的作用:
db.getCollection('people').mapReduce(
function() {
emit(null, Object.keys(this.achievements).map(
k => ({ [k]: this.achievements[k] ? 1 : 0 })
).reduce((acc,curr) => Object.assign(acc,curr),{}));
},
function(key,values) {
return [].concat.apply([],values.map(
v => Object.keys(v).map( k => ({ k, v: v[k] }))
)).reduce((acc,curr) => Object.assign(acc,{
[curr.k]: ( acc.hasOwnProperty(curr.k) )
? acc[curr.k] + curr.v
: curr.v
}),{});
},
{ "out": { "inline": 1 } }
)