使用mongodb聚合框架计算频率

我试图根据10秒的间隔计算我的数据库中的文件的频率。

这是我的数据库对象的样子:

[ { 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秒的时间间隔?

如果仅仅是在10秒内获得东西,你可以做一些math运算,并通过聚合运行:

 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分钟的math来分解它们。

我认为这是合理的,因为它使用聚合,将是最快的跑步者。 如果你真的需要你的序列如图所示从最初匹配的时间开始运行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秒的时间间隔内发射date,从第一个date开始,然后在每次发现超出范围时增加间隔

现在你需要一个减速器:

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

很简单。 只要返回传入的数组的长度。

因为mapReduce的工作方式,所有没有超过一个值的东西都不会传递给reducer,所以用finalize来清理它:

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

然后运行它来获得结果。 请注意传递映射器中使用的全局variables的“范围”部分:

 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 ); }