在Mongo中分箱和制表(唯一/计数)

Jer*_*oen 8 mongoose mongodb nosql

我正在寻找一种使用Mongo生成一些摘要统计数据的方法.假设我有一个包含许多表单记录的集合

{"name" : "Jeroen", "gender" : "m", "age" :27.53 }
Run Code Online (Sandbox Code Playgroud)

现在我想得到性别和年龄的分布.假设性别,只有价值"m""f".在我的收藏中获得男性和女性总数最有效的方法是什么?

而对于年龄,有没有办法做一些'binning'并给我一个像汇总的直方图; 即年龄在间隔内的记录数量:[0, 2), [2, 4), [4, 6) ...等等?

Jen*_*nna 20

我刚刚尝试了MongoDB 2.2版(2.2.0-rc0已经发布)中可用的新聚合框架,它应该比map reduce具有更高的性能,因为它不依赖于Javascript.

输入数据:

{ "_id" : 1, "age" : 22.34, "gender" : "f" }
{ "_id" : 2, "age" : 23.9, "gender" : "f" }
{ "_id" : 3, "age" : 27.4, "gender" : "f" }
{ "_id" : 4, "age" : 26.9, "gender" : "m" }
{ "_id" : 5, "age" : 26, "gender" : "m" }
Run Code Online (Sandbox Code Playgroud)

性别聚合命令:

db.collection.aggregate(
   {$project: {gender:1}},
   {$group: {
        _id: "$gender",
        count: {$sum: 1}
   }})
Run Code Online (Sandbox Code Playgroud)

结果:

{"result" : 
   [
     {"_id" : "m", "count" : 2},
     {"_id" : "f", "count" : 3}
   ],
   "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)

为了获得箱子的年龄:

db.collection.aggregate(
   {$project: {
        ageLowerBound: {$subtract:["$age", {$mod:["$age",2]}]}}
   },
   {$group: {
       _id:"$ageLowerBound", 
       count:{$sum:1}
   }
})
Run Code Online (Sandbox Code Playgroud)

结果:

{"result" : 
    [
       {"_id" : 26, "count" : 3},
       {"_id" : 22, "count" : 2}
    ],
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)

  • 对于不是某个数的倍数的任意二进制数,你可以使用[$ cond](http://docs.mongodb.org/manual/reference/operator/aggregation/cond/#exp._S_cond)虽然语法很糟糕:$ project:{ageLowerBound:{$ cond:[{$ lt:[$ age,2]},"0",{$ cond:[{$ lt:[$ age,4]},"2"," 4"]}]}} ......或类似的东西. (2认同)

Jer*_*oen 2

康斯坦丁的回答是正确的。MapReduce 完成了工作。这是完整的解决方案,以防其他人觉得这很有趣。

为了统计性别,地图功能键是this.gender每条记录的属性。然后,reduce 函数将它们简单地相加:

// count genders
db.persons.mapReduce(
    function(){
        emit(this["gender"], {count: 1})
    }, function(key, values){
        var result = {count: 0};
        values.forEach(function(value) {
            result.count += value.count;
        });
        return result;
    }, {out: { inline : 1}}
);
Run Code Online (Sandbox Code Playgroud)

为了进行分箱,我们将映射函数中的键设置为向下舍入到最接近的除以二。因此,例如 10 到 11.9999 之间的任何值都将获得相同的密钥"10-12"。然后我们再次简单地将它们相加:

db.responses.mapReduce(
    function(){
        var x = Math.floor(this["age"]/2)*2;
        var key = x + "-" + (x+2);
        emit(key, {count: 1})
    }, function(state, values){
        var result = {count: 0};
        values.forEach(function(value) {
            result.count += value.count;
        });
        return result;
    }, {out: { inline : 1}}
);
Run Code Online (Sandbox Code Playgroud)