2014-04-04 93 views
2

我试图根据10秒的间隔计算我的数据库中文档的频率。使用mongodb聚合框架计算频率

这是我的数据库对象看起来怎么样:

[ 
    { 
    created_at: "2014-03-31T22:30:48.000Z", 
    id: 450762158586880000, 
    _id: "5339ec9808eb125965f2eae1" 
    }, 
    { 
    created_at: "2014-03-31T22:30:48.000Z", 
    id: 450762160407597060, 
    _id: "5339ec9808eb125965f2eae2" 
    }, 
    { 
    created_at: "2014-03-31T22:30:49.000Z", 
    id: 450762163482017800, 
    _id: "5339ec9908eb125965f2eae3" 
    }, 
    { 
    created_at: "2014-03-31T22:30:49.000Z", 
    id: 450762166367707140, 
    _id: "5339ec9908eb125965f2eae4" 
    }, 
    { 
    created_at: "2014-03-31T22:30:50.000Z", 
    id: 450762167412064260, 
    _id: "5339ec9a08eb125965f2eae5" 
    } 
] 

我已成功地显示在给定的时间间隔的频率,但我想获得,每10秒。所以最好我的JSON看起来像:

[ 
    { 
    time_from: "2014-03-31T22:30:48.000Z", 
    time_to: "2014-03-31T22:30:58.000Z", 
    count: 6 
    }, 
    { 
    time_from: "2014-03-31T22:30:58.000Z", 
    time_to: "2014-03-31T22:31:08.000Z", 
    count: 3 
    }, 
    { 
    time_from: "2014-03-31T22:31:08.000Z", 
    time_to: "2014-03-31T22:31:18.000Z", 
    count: 10 
    }, 
    { 
    time_from: "2014-03-31T22:31:18.000Z", 
    time_to: "2014-03-31T22:31:28.000Z", 
    count: 1 
    }, 
    { 
    time_from: "2014-03-31T22:31:28.000Z", 
    time_to: "2014-03-31T22:31:38.000Z", 
    count: 3 
    } 
] 

这是我迄今所做的:

exports.findAll = function (req, res) { 
    db.collection(collection_name, function (err, collection) { 
     collection.find().toArray(function (err, items) { 
      collection.find().sort({"_id": 1}).limit(1).toArray(function (err, doc) { 
       var interval = 100000; // in milliseconds 
       var startTime = doc[0].created_at; 
       var endTime = new Date(+startTime + interval); 

       collection.aggregate([ 
        {$match: {"created_at": {$gte: startTime, $lt: endTime}}}, 
        {$group: {"_id": 1, "count":{$sum: 1}}} 
       ], function(err, result){ 
        console.log(result); 
        res.send(result); 
       }); 
      }); 
     }) 
    }); 
}; 

,这是这个结果:

[ 
    { 
    _id: 1, 
    count: 247 
    } 
] 

编辑:

collection.aggregate([ 
        { $group: { 
         _id: { 
          year: { '$year': '$created_at'}, 
          month: {'$month': '$created_at'}, 
          day: {'$dayOfMonth': '$created_at'}, 
          hour: {'$hour': '$created_at'}, 
          minute: {'$minute': '$created_at'}, 
          second: {'$second': '$created_at'} 
         }, 
         count: { $sum : 1 } 
        } } 
       ], function (err, result) { 
        console.log(result); 
        res.send(result); 
       }); 

其结果是:

[ 
    { 
    _id: { 
     year: 2014, 
     month: 3, 
     day: 31, 
     hour: 22, 
     minute: 37, 
     second: 10 
    }, 
    count: 6 
    }, ... 

新的进展,现在我怎么会显示它在10秒的时间间隔?

回答

1

如果它只是约10秒的时间间隔内得到的东西,你可以做一个小的数学,并通过总运行此:所以

db.collection.aggregate([ 
    { "$group": { 
     "_id": { 
      "year": { "$year": "$created_at" }, 
      "month":{ "$month": "$created_at" }, 
      "day": { "$dayOfMonth": "$created_at" }, 
      "hour": { "$hour": "$created_at" }, 
      "minute": { "$minute": "$created_at" }, 
      "second": { "$subtract": [ 
       { "$second": "$created_at" }, 
       { "$mod": [ 
        { "$second": "$created_at" }, 
        10 
       ]} 
      ]} 
     }, 
     "count": { "$sum" : 1 } 
    }} 
]) 

,打破下来,以10秒的间隔一分钟他们在哪里出现了一个10码的数学模型。

我认为这是合理的,并且因为它使用聚合,所以会是跑得最快的。如果你真的需要如从初次匹配时间是跑10秒钟内你的序列,那么你可以用MapReduce的过程:

首先映射器:

var mapper = function() { 

    if (this.created_at.getTime() > (last_date + 10000)) { 
     if (last_date == 0) { 
      last_date = this.created_at.getTime(); 
     } else { 
      last_date += 10000; 
     } 
    } 

    emit(
     { 
      start: new Date(last_date), 
      end: new Date(last_date + 10000) 
     }, 
     this.created_at 
    ); 

} 

所以这是要发出10秒时间间隔内的日期,开始第一次约会,然后增加间隔每次的东西被发现超出范围

现在你需要一个减速机:

var reducer = function (key, values) { 
    return values.length; 
}; 

非常简单。只返回传入的数组的长度

因为MapReduce的工作方式是这样,任何不是有一个以上的值不会传递到减速,因此与敲定清理它。

var finalize = function (key, value) { 
    if (typeof(value) == "object") { 
     value = 1; 
    } 
    return value; 
}; 

然后只需运行它即可获得结果。请注意,在映射器中使用的是通过一个全局变量的“范围”一节:

db.collection.mapReduce(
    mapper, 
    reducer, 
    { 
     "out": { "inline": 1 }, 
     "scope": { "last_date": 0 }, 
     "finalize": finalize 
    } 
) 

每种方法很可能会给略微不同的结果,但就是这一点。这取决于你真正想使用哪一个。


考虑您的评论,你既可以“考察”无论从语句的输出和“填补空白”编程,因为它是。我通常更喜欢这个选项,但它不是我的程序,我不知道你试图从这个查询中检索的系列有多大。

在服务器端,您可以修补的“映射”做一些像这样的:

var mapper = function() { 

    if (this.created_at.getTime() > (last_date + 10000)) { 

     if (last_date == 0) { 
      last_date = this.created_at.getTime(); 
     } else { 
      // Patching for empty blocks 
      var times = Math.floor( 
       (this.created_at.getTime() - last_date)/10000 
      ); 

      if (times > 1) { 
       for (var i=1; i < times; i++) { 
        last_date += 10000; 
        emit(
         { 
          start: new Date(last_date), 
          end: new Date(last_date + 10000) 
         }, 
         0 
        ); 
       } 
      } 
      // End patch 
      last_date += 10000; 
     } 
    } 

    emit(
     { 
      start: new Date(last_date), 
      end: new Date(last_date + 10000) 
     }, 
     this.created_at 
    ); 

} 
+0

这是90%的什么我正在寻找,但我想获得价值= 0,没有数据。聚合只是跳过这个地方,即使文档没有在间隔中创建,mapReduce似乎也会使值= 1。有任何想法吗?再次感谢您发布的内容! – knowbody

+0

@knowbody我几乎只是有一个评论,但实际的内容被添加到解释和解决的答案。 –

+0

再次感谢你。这对我帮助很大! – knowbody