我有一个用户集合,如下
{
"id":"id here",
name: 'name here',
height: 'height here',
weight: 'weight here',
lastLogin:[array of login dates],
messagesSentOn: [array of messages sent date]
}
我需要找到所有上个月登录过但不止一次的用户,以及上个月发送了超过25条消息的用户,他们的体重超过50,身高超过5英寸。如何在mongodb中为上述情况编写一个map reduce函数?
我在shell中提供了一个示例。我不确定MR是否是解决这个问题的最佳解决方案,我鼓励您考虑其他解决方案,以避免使用单线程Javascript。例如,您可以存储一个额外的字段,该字段只包含当月的登录名或消息。每次添加登录和/或消息时,都会增加一个计数器字段。此架构允许您在不使用聚合命令的情况下查找匹配的文档。
您还应该研究一下新的聚合框架,它将在MongoDB 2.2版(即将推出)中提供:http://docs.mongodb.org/manual/applications/aggregation/
最后一点要注意的是,为了提高性能,您应该确保在MR命令中包含一个查询,以剔除不匹配的文档(请参阅下面的示例)。
输入文档:
{ "_id" : 1, "name" : "Jenna", "height" : 100, "weight" : 51, "lastLogin" : [ 1, 2, 3, 4 ], "messageSentOn" : [ 4, 5, 5, 7 ] }
{ "_id" : 2, "name" : "Jim", "height" : 60, "weight" : 49, "lastLogin" : [ 2, 4 ], "messageSentOn" : [ 5, 6 ] }
{ "_id" : 3, "name" : "Jane", "height" : 90, "weight" : 60, "lastLogin" : [ 1 ], "messageSentOn" : [ 3, 6 ] }
{ "_id" : 4, "name" : "Joe", "height" : 70, "weight" : 65, "lastLogin" : [ 5, 6, 7 ], "messageSentOn" : [ 3, 6, 7 ] }
MR功能:
map = function(){
var monthLogins = 0;
var monthMessages = 0;
var monthDate = 2;
for(var i=0; i<this.lastLogin.length; i++){
if(this.lastLogin[i] > monthDate){
monthLogins++;
}
}
for(var i=0; i<this.messageSentOn.length; i++){
if(this.messageSentOn[i] > monthDate){
monthMessages++;
}
}
if(monthLogins > 1 && monthMessages > 2)
{ emit(this._id, null);
}
}
reduce = function (key, values) {
//won't be called because a single document is emitted for each key
}
MR命令:
db.collection.mapReduce(map, reduce, {query: {weight: {$gt : 50}, height: {$gt: 5}, lastLogin: {$gt: 2}}, out: {inline:1}})
输出:
{"_id" : 1, "value" : null},
{"_id" : 4, "value" : null}