我在这个问题上需要帮助mongo:我有收集统计信息(UserId,EventId,Count,Date) 在集合中是数据
1 | 1 | 10 | 01.01.2012
1 | 1 | 15 | 01.02.2012
1 | 2 | 12 | 01.01.2012
2 | 1 | 5 | 01.01.2012
3 | 2 | 10 | 01.01.2012
我需要这个结果
1 | 25 | 12
2 | 5 | 0
3 | 0 | 10
它可能在没有Map Reduce的Mongo中? 谢谢你的帮助。
答案 0 :(得分:4)
使用aggregate()
来完成工作更容易,也更快!
我们将使用$project
为每个事件创建一个计数器字段,如果事件匹配,则填写文档中的计数,否则为零。然后我们将$group
通过user-id,总结所有事件计数器。
为了便于解释,让我首先展示一下你的例子中两个不同事件(1和2)的硬编码方式:
db.xx.aggregate([
{ $project: { userid:1,
cnt_e1: { $cond: [ { $eq: [ "$event", 1 ] }, "$count", 0 ] },
cnt_e2: { $cond: [ { $eq: [ "$event", 2 ] }, "$count", 0 ] },
} },
{ $group: { _id: "$userid", cnt_e1: { $sum: "$cnt_e1" }, cnt_e2: { $sum: "$cnt_e2" } } },
{ $sort: { _id: 1 } },
])
对于给定的集合:
> db.xx.find({},{_id:0})
{ "userid" : 1, "event" : 1, "count" : 10 }
{ "userid" : 1, "event" : 1, "count" : 15 }
{ "userid" : 1, "event" : 2, "count" : 12 }
{ "userid" : 2, "event" : 1, "count" : 5 }
{ "userid" : 3, "event" : 2, "count" : 10 }
结果是:
{
"result" : [
{
"_id" : 1,
"cnt_e1" : 25,
"cnt_e2" : 12
},
{
"_id" : 2,
"cnt_e1" : 5,
"cnt_e2" : 0
},
{
"_id" : 3,
"cnt_e1" : 0,
"cnt_e2" : 10
}
],
"ok" : 1
}
要为变量事件完成此操作,我们必须生成投影和分组。我们将使用distinct()
命令获取所有可能事件的数组(您可能希望在“event”上定义索引)。然后我们通过遍历数组创建两个语句作为JSON对象:
project = {};
project.$project = {};
project.$project.userid = 1;
group = {};
group.$group = {};
group.$group._id = '$userid'
events = db.xx.distinct( "event" );
events.forEach( function( e ) {
field = "cnt_e" + e;
eval("project.$project." + field + " = {}");
eval("project.$project." + field + ".$cond = []");
eval("project.$project." + field + ".$cond[0] = {}");
eval("project.$project." + field + ".$cond[0].$eq = []");
eval("project.$project." + field + ".$cond[0].$eq[0] = '$event'");
eval("project.$project." + field + ".$cond[0].$eq[1] = " + e );
eval("project.$project." + field + ".$cond[1] = '$count'");
eval("project.$project." + field + ".$cond[2] = 0");
eval("group.$group." + field + " = {}");
eval("group.$group." + field + ".$sum = '$" + field + "'");
});
//printjson(project);
//printjson(group);
db.xx.aggregate([
project,
group,
{ $sort: { _id: 1 } },
])
结果与上述相同。
注意:以上适用于数字事件。如果它们是字符串,则必须调整发生器。
乍一看,这看起来可能比@Philipp的mapReduce更复杂。但是这不会返回每个用户的所有事件 - 只有那些有计数的事件。对于完整的垂直到水平映射,您还必须生成映射和reduce函数。有关aggregate()的更多信息,请参阅http://docs.mongodb.org/manual/aggregation/
答案 1 :(得分:0)
您必须使用MapReduce操作执行此操作。
你的地图功能如下所示:(未经测试!):
var mapFunction = function() {
var ret = {};
ret["Count_Event_" + this.EventId] = this.Count;
emit(this.UserId, ret);
};
这会发出一系列由UserId和一个对象组成的对,这些对象具有一个不同命名的属性,其中count为值。
你的reduce函数会将结果合并为一个(未经测试 - 我不确定你是否只能增加一个不存在的属性而我现在无法测试它):
var reduceFunction = function(UserId, values_array) {
var ret = {};
for (var i = 0; i < values_array.length; i++) {
var values = values_array[i];
for (key in values) {
ret[key] += values[key]; // Can you increment a non-existing attribute? Not sure, try it, please.
}
}
return ret;
};
然后你这样称呼:
db.yourCollection.mapReduce(
mapFunction,
reduceFunction,
out: { inline: 1 }
)
行out: { inline: 1 }
将结果输出到控制台。通常,您使用MapReduce创建包含结果的新集合。有关详细信息,请参阅the documentation。