我有一个集合,其中start_time和end_time表示会话我需要在给定的小时内计算最大并发会话。
类似于按小时聚合和分组。
最有效的方法是什么?
您的查询将这样做:
db.collection_name.aggregate ([{$组:{_id: $小时no_of_sessions:{$金额:1}}}))
$hour是您的时间变量(假设您只是存储小时,如果不是,您可以应用(hour: {$hour: "$date"})函数从date中获取它)。如果时间是1:01到2:59,那么您需要将_id定义为复合键。例如:_id: {start_time: $start_time, end_time: $end_time}.
为了得到更具体的答案,请给出确切的情况。
干杯!
这种聚合类型的问题在于,具有"start_time"one_answers"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或开始时间在当前日期之前的endDay,或者end_time不存在
从开始时间开始,以一个小时的增量循环,直到到达结束时间或到达差一天的时间。
每次排放都是一个"计数",表示与起始日的小时差。减少到每小时总数
有一个可选的部分,也将发出0
值每小时的一天,因此,如果没有数据被记录,那么至少有输出该小时作为0
。