所有子文档密钥的汇总总数

时间:2017-09-18 07:50:40

标签: mongodb aggregation-framework

我有以下几十个成就字段的用户记录集:

{
  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,
  ...
  ...
}

1 个答案:

答案 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 } }
)