我有一个集合,其start_time和end_time表示会话 我需要计算给定时间内的最大并发会话数。
像小时一样的聚合和小组。
最有效的方法是什么?
答案 0 :(得分:0)
您执行此操作的查询将类似于:
<强> db.collection_name.aggregate( [{$ group:{_ id:$ hour,no_of_sessions:{$ sum:1}}}] )强>
此处: $ hour 是您的时间变量(假设您只是存储小时,如果没有,您可以申请(小时:{$小时:&#34; $ date&#34;})功能从日期开始。)
如果小时数如1:01到2:59那么您需要将_id定义为复合键。类似于:_id:{start_time:$ start_time,end_time:$ end_time}。
要获得更具体的答案,请提供具体案例。
干杯!
答案 1 :(得分:0)
这种聚合的问题在于,具有“start_time”和“end_time”的“会话”实际上因此可以“发出”跨越每个分组小时的小时数,因此它存在于超过一小时的时间会议结束前的一段时间。这可能会持续数小时
这里的另一个主要问题是会话可能会在您想要查看的时间段之前“开始”,或者甚至在指定范围之后“结束”,例如一天。在这里,您需要考虑到您通常会寻找一个小于您正在查看的那天结束的“start_time”,并且“end_time”大于您正在查看的那天的开始。
即便如此,还有其他一些考虑因素,例如在分析时是否会有“end_time”?通常,处理此问题的最佳方法是考虑合理的“会话生命周期”值,并将其纳入基本查询选择中。
因此,在使用一些变量时,我们基本上会选择“基本标准”:
var startDay = new Date("2015-08-30"),
endDay = new Date("2015-08-31"),
oneHour = 1000*60*60,
sessionTime = 3*oneHour;
var query = {
"start_time": {
"$gte": new Date(startDay.valueOf()-sessionTime),
"$lt": endDay
},
"$or": [
{ "end_time": { "$exists": false } },
{ "end_time": null },
{ "end_time": {
"$lt": new Date(endDay.valueOf()+sessionTime),
"$gte": startDay
}}
]
};
例如,在这里使用3小时窗口,还包括当天之外的已找到日期,以包含在“可能”输出中。
接下来考虑一些数据作为样本:
{ "_id": 1, "start_time": new Date("2015-08-29T23:30"), "end_time": new Date("2015-08-29T23:45") },
{ "_id": 2, "start_time": new Date("2015-08-29T23:30"), "end_time": new Date("2015-08-30T00:45") },
{ "_id": 3, "start_time": new Date("2015-08-30T00:30"), "end_time": new Date("2015-08-30T01:30") },
{ "_id": 4, "start_time": new Date("2015-08-30T01:30"), "end_time": new Date("2015-08-30T01:45") },
{ "_id": 5, "start_time": new Date("2015-08-30T01:30"), "end_time": new Date("2015-08-30T03:45") },
{ "_id": 6, "start_time": new Date("2015-08-30T01:45"), "end_time": new Date("2015-08-30T02:30") },
{ "_id": 7, "start_time": new Date("2015-08-30T23:30"), "end_time": null },
{ "_id": 8, "start_time": new Date("2015-08-30T23:30") },
{ "_id": 9, "start_time": new Date("2015-08-31T01:30") }
如果我们查看日期范围的标准和一般查询选择,那么您可以预期在我们正在查看的那一天会考虑记录2到8,因为它们要么在当天“结束”,要么“开始” “在白天。 “会话窗口”主要是因为某些数据没有“end_time”,是null
或不存在。该“窗口”有助于过滤掉可能来自最近查看日期的其他无关数据,并保持合理的大小。
快速视觉扫描应告诉您每小时的计数应为:
0: 2
1: 4,
2: 2,
3: 1
23: 2
使用mapReduce比使用任何其他聚合介质更好地处理实际过程。这是因为所需的条件逻辑允许“单个文档”被“发出”为对多个时段有效的值。所以这里需要一个继承的“循环”
db.sessions.mapReduce(
function() {
var oneHour = 1000*60*60,
start = (this.start_time > startDay)
? ( this.start_time.valueOf() - ( this.start_time.valueOf() % oneHour ))
: startDay,
end = (this.hasOwnProperty("end_time") && this.end_time != null)
? ( this.end_time.valueOf() - ( this.end_time.valueOf() % oneHour ))
: endDay;
// Uncomment to Emit blank values for each hour on first iteration
/*
if ( count == 0 ) {
for ( var x = 1; x <= 24; x++ ) {
emit(x,0);
}
count++;
}
*/
for ( var y = start; y <= end && (y-startDay)/oneHour < 24; y+= oneHour) {
emit(
(y-startDay ==0) ? 0 : ((y-startDay)/oneHour)
,1
);
}
},
function(key,values) {
return Array.sum(values);
},
{
"out": { "inline": 1 },
"scope": {
"startDay": startDay.valueOf(),
"endDay": endDay.valueOf(),
"count": 0
},
"query": query
}
)
结合前面设置的变量,这将正确计算每小时当前运行的会话数:
"results" : [
{
"_id" : 0,
"value" : 2
},
{
"_id" : 1,
"value" : 4
},
{
"_id" : 2,
"value" : 2
},
{
"_id" : 3,
"value" : 1
},
{
"_id" : 23,
"value" : 2
}
],
每条记录的基本操作是:
将开始和结束时间分别计算为1小时
将每个值替换为正在查看的日期的startDay或者当前日期之前或者end_time不存在的endDay
从开始时间开始,以1小时为增量循环,直到达到结束时间或达到一天的差异。每次发射都是与startDay的小时差异的“计数”。
减少每小时的总数
有一个可选部分,它还会为一天中的每个小时发出0
个值,这样如果没有记录数据,那么至少那个小时的输出为0
。