我有以下文档结构
{
"app_id": "DHJFK67JDSJjdasj909",
"date": ISODate("2014-08-07T00:00:00.000Z"),
"event_count": 100,
"events": [
{ "type": 0, "value": 12 },
{ "type": 10, "value": 24 },
{ "type": 20, "value": 36 },
{ "type": 30, "value": 43 }
],
"unique_events": [
{ "type": 0, "value": 5 },
{ "type": 10, "value": 8 },
{ "type": 20, "value": 12 },
{ "type": 30, "value": 56 }
]
}
我正在尝试获取event_counts的总和以及每种类型的unique_events和事件的值。这是我期望的输出类型,其中event_count以及每个事件和unique_events值已按类型求和。
{
"app_id": "DHJFK67JDSJjdasj909",
"date": ISODate("2014-08-07T00:00:00.000Z"),
"event_count": 4345,
"events": [
{ "type": 0, "value": 624 },
{ "type": 10, "value": 234 },
{ "type": 20, "value": 353 },
{ "type": 30, "value": 472 }
],
"unique_events": [
{ "type": 0, "value": 433 },
{ "type": 10, "value": 554 },
{ "type": 20, "value": 645 },
{ "type": 30, "value": 732 }
]
}
这是我的查询
db.events.aggregate([
{ "$unwind": "$events" },
{ "$group": {
"_id": {
"app_id": "$app_id",
"type": "$events.type"
"unique_type": "$unique_events.type"
},
"event_count": { "$sum": "$event_count" },
"event_value": { "$sum": "$events.value" },
"unique_event_value": { "$sum": "$unique_events.value" }
}},
{ "$group": {
"_id": "$_id.app_id",
"event_count": { "$sum": "$event_count" },
"events": { "$push": { "type": "$_id.type", "value": "$event_value" } }
"unique_events": { "$push": { "type": "$_id.unique_type", "value": "$unique_event_value" } }
}}
])
问题是使用两个$ unwinds然后按事件和unique_events进行分组会导致$ sum被复合并且太大。有什么方法可以使用mongo解决这个问题,或者我必须运行两个查询然后在代码中合并两个结果集。
由于
伊尔凡
答案 0 :(得分:8)
这真的很简单,为了对每个数组的结果求和,它只是在辨别哪个是哪个和"组合元素"的问题。简而言之,你可能应该在你的文档中这样做,因为从第一个流水线阶段可以看出这一点。
因此,对于MongoDB 2.6及更高版本,有一些辅助方法:
db.events.aggregate([
{ "$project": {
"app_id": 1,
"event_count": 1,
"all_events": {
"$setUnion": [
{ "$map": {
"input": "$events",
"as": "el",
"in": {
"type": "$$el.type",
"value": "$$el.value",
"class": { "$literal": "A" }
}
}},
{ "$map": {
"input": "$unique_events",
"as": "el",
"in": {
"type": "$$el.type",
"value": "$$el.value",
"class": { "$literal": "B" }
}
}}
]
}
}},
{ "$unwind": "$all_events" },
{ "$group": {
"_id": {
"app_id": "$app_id",
"class": "$all_events.class",
"type": "$all_events.type"
},
"event_count": { "$sum": "$event_count" },
"value": { "$sum": "$all_events.value" }
}},
{ "$group": {
"_id": "$_id.app_id",
"event_count": { "$sum": "$event_count" },
"events": {
"$push": {
"$cond": [
{ "$eq": [ "$_id.class", "A" ] },
{ "type": "$_id.type", "value": "$value" },
false
]
}
},
"unique_events": {
"$push": {
"$cond": [
{ "$eq": [ "$_id.class", "B" ] },
{ "type": "$_id.type", "value": "$value" },
false
]
}
}
}},
{ "$project": {
"event_count": 1,
"events": { "$setDifference": [ "$events", [false] ] },
"unique_events": {
"$setDifference": [ "$unique_events", [false] ]
}
}}
])
主要位于$setUnion
和$setDifference
运营商中。另一个ccase是$map
,它处理数组到位。整个过程就是在不使用$unwind
的情况下对数组进行操作。但这些当然可以在以前的版本中完成,只需要做一些工作:
db.events.aggregate([
{ "$unwind": "$events" },
{ "$group": {
"_id": "$_id",
"app_id": { "$first": "$app_id" },
"event_count": { "$first": "$event_count" },
"events": {
"$push": {
"type": "$events.type",
"value": "$events.value",
"class": { "$const": "A" }
}
},
"unique_events": { "$first": "$unique_events" }
}},
{ "$unwind": "$unique_events" },
{ "$group": {
"_id": "$_id",
"app_id": { "$first": "$app_id" },
"event_count": { "$first": "$event_count" },
"events": { "$first": "$events" },
"unique_events": {
"$push": {
"type": "$unique_events.type",
"value": "$unique_events.value",
"class": { "$const": "B" }
}
}
}},
{ "$project": {
"app_id": 1,
"event_count": 1,
"events": 1,
"unique_events": 1,
"type": { "$const": [ "A","B" ] }
}},
{ "$unwind": "$type" },
{ "$unwind": "$events" },
{ "$unwind": "$unique_events" },
{ "$group": {
"_id": "$_id",
"app_id": { "$first": "$app_id" },
"event_count": { "$first": "$event_count" },
"all_events": {
"$addToSet": {
"$cond": [
{ "$eq": [ "$events.class", "$type" ] },
{
"type": "$events.type",
"value": "$events.value",
"class": "$events.class"
},
{
"type": "$unique_events.type",
"value": "$unique_events.value",
"class": "$unique_events.class"
}
]
}
}
}},
{ "$unwind": "$all_events" },
{ "$group": {
"_id": {
"app_id": "$app_id",
"class": "$all_events.class",
"type": "$all_events.type"
},
"event_count": { "$sum": "$event_count" },
"value": { "$sum": "$all_events.value" }
}},
{ "$group": {
"_id": "$_id.app_id",
"event_count": { "$sum": "$event_count" },
"events": {
"$push": {
"$cond": [
{ "$eq": [ "$_id.class", "A" ] },
{ "type": "$_id.type", "value": "$value" },
false
]
}
},
"unique_events": {
"$push": {
"$cond": [
{ "$eq": [ "$_id.class", "B" ] },
{ "type": "$_id.type", "value": "$value" },
false
]
}
}
}},
{ "$unwind": "$events" },
{ "$match": { "events": { "$ne": false } } },
{ "$group": {
"_id": "$_id",
"event_count": { "$first": "$event_count" },
"events": { "$push": "$events" },
"unique_events": { "$first": "$unique_events" }
}},
{ "$unwind": "$unique_events" },
{ "$match": { "unique_events": { "$ne": false } } },
{ "$group": {
"_id": "$_id",
"event_count": { "$first": "$event_count" },
"events": { "$first": "$events" },
"unique_events": { "$push": "$unique_events" }
}}
])
这可以获得你想要的结果,每个数组都是"总结"一起以及主人" event_count"结果正确。
您可能应该考虑将两个阵列与管道中使用的类似标识符进行组合,如图所示。这部分是工作的一半。另一半考虑您可能应该将预聚合结果存储在某个集合中,以获得最佳应用程序性能。
答案 1 :(得分:2)
您可以执行以下地图缩小:
它不是动态解决方案,我为每个variable
和events
创建了unique_events
我使用collection
函数中的out: "session_stat"
在不同的mapReduce
中保存了输出。
var mapFunction = function() {
var key = this.app_id;
var value = {
event_count: this.event_count,
events: this.events,
unique_events: this.unique_events
};
emit( key, value );
};
var reduceFunction = function(key, values) {
var reducedObject = {
app_id: key,
events_wise_total: 0,
unique_events_wise_total:0,
total_event_count:0
};
var events_wise_total = [];
var events_0_total = { type:0, value :0};
var events_10_total = { type:10, value :0};
var events_20_total = { type:20, value :0};
var events_30_total = { type:30, value :0};
var unique_events_wise_total = [];
var unique_events_0_total = { type:0, value :0};
var unique_events_10_total = { type:10, value :0};
var unique_events_20_total = { type:20, value :0};
var unique_events_30_total = { type:30, value :0};
var total_event_count = 0;
values.forEach( function(value) {
total_event_count += value.event_count;
var events = value.events;
events.forEach(function(event){
if(event.type == 0){events_0_total.value += event.value;}
if(event.type == 10){events_10_total.value += event.value;}
if(event.type == 20){events_20_total.value += event.value;}
if(event.type == 30){events_30_total.value += event.value;}
});
var unique_events = value.unique_events;
unique_events.forEach(function(unique_event){
if(unique_event.type == 0){unique_events_0_total.value += unique_event.value;}
if(unique_event.type == 10){unique_events_10_total.value += unique_event.value;}
if(unique_event.type == 20){unique_events_20_total.value += unique_event.value;}
if(unique_event.type == 30){unique_events_30_total.value += unique_event.value;}
});
}
);
events_wise_total.push(events_0_total);
events_wise_total.push(events_10_total);
events_wise_total.push(events_20_total);
events_wise_total.push(events_30_total);
unique_events_wise_total.push(unique_events_0_total);
unique_events_wise_total.push(unique_events_10_total);
unique_events_wise_total.push(unique_events_20_total);
unique_events_wise_total.push(unique_events_30_total);
reducedObject.events_wise_total = events_wise_total;
reducedObject.unique_events_wise_total = unique_events_wise_total;
reducedObject.total_event_count = total_event_count;
return reducedObject;
};
var finalizeFunction = function (key, reducedValue) {
return reducedValue;
};
db.GroupBy.mapReduce(
mapFunction,
reduceFunction,
{
out: "session_stat",
finalize: finalizeFunction
});
希望它可能有用