MongoDB计数并发

时间:2015-09-01 04:00:29

标签: mongodb mapreduce mongodb-query aggregation-framework

我有一个集合,其start_time和end_time表示会话 我需要计算给定时间内的最大并发会话数。

像小时一样的聚合和小组。

最有效的方法是什么?

2 个答案:

答案 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