我有一些日志数据存储在一个mongo集合中,其中包括作为request_id的基本信息以及它被添加到集合中的时间,例如:
{
"_id" : ObjectId("55ae6ea558a5d3fe018b4568"),
"request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3",
"time" : ISODate("2015-07-21T16:00:00.00Z")
}
我想知道是否可以使用聚合框架来聚合一些统计数据。我想要得到最近X小时内每隔N分钟创建的对象的计数。
因此,在过去1小时中,每隔10分钟我需要的输出应该如下所示:
{ "_id" : 0, "time" : ISODate("2015-07-21T15:00:00.00Z"), "count" : 67 }
{ "_id" : 0, "time" : ISODate("2015-07-21T15:10:00.00Z"), "count" : 113 }
{ "_id" : 0, "time" : ISODate("2015-07-21T15:20:00.00Z"), "count" : 40 }
{ "_id" : 0, "time" : ISODate("2015-07-21T15:30:00.00Z"), "count" : 10 }
{ "_id" : 0, "time" : ISODate("2015-07-21T15:40:00.00Z"), "count" : 32 }
{ "_id" : 0, "time" : ISODate("2015-07-21T15:50:00.00Z"), "count" : 34 }
我将用它来获取图表的数据。
任何建议都是感激的!
根据哪种输出格式最适合您的需要,有几种方法可以实现这一点。主要注意事项是,使用"聚合框架"本身,您实际上不能返回"cast"作为日期的东西,但是当在API中处理结果时,您可以获得容易重构为Date
对象的值。
db.collection.aggregate([
{ "$match": {
"time": { "$gte": startDate, "$lt": endDate }
}},
{ "$group": {
"_id": {
"year": { "$year": "$time" },
"dayOfYear": { "$dayOfYear": "$time" },
"hour": { "$hour": "$time" },
"minute": {
"$subtract": [
{ "$minute": "$time" },
{ "$mod": [ { "$minute": "$time" }, 10 ] }
]
}
},
"count": { "$sum": 1 }
}}
])
返回_id
的组合键,其中包含您想要的"日期"的所有值。或者,如果只是在一个"小时"之内,那么总是使用"分钟"部分,并根据范围选择的startDate
计算出实际日期。
或者你可以使用简单的"Date math"来获得自"epoch"以来的毫秒数,这也可以直接提供给日期构造器。
db.collection.aggregate([
{ "$match": {
"time": { "$gte": startDate, "$lt": endDate }
}},
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$time", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$time", new Date(0) ] },
1000 * 60 * 10
]}
]
},
"count": { "$sum": 1 }
}}
])
在所有情况下,不想要做的是在实际应用 $group
之前使用 $project
。作为一个"管道阶段",$project
必须"循环"遍历选中的所有文档,并"转换"内容。
这会占用时间,并添加到查询的执行总数中。您可以简单地直接应用到$group
,如所示。
或者,如果你真的是"纯"的Date
对象返回没有后处理,那么你总是可以使用"mapReduce",因为JavaScript函数实际上允许重转换为日期,但比聚合框架慢,当然没有游标响应:
db.collection.mapReduce(
function() {
var date = new Date(
this.time.valueOf()
- ( this.time.valueOf() % ( 1000 * 60 * 10 ) )
);
emit(date,1);
},
function(key,values) {
return Array.sum(values);
},
{ "out": { "inline": 1 } }
)
您最好的选择是使用聚合,因为转换响应非常容易:
db.collection.aggregate([
{ "$match": {
"time": { "$gte": startDate, "$lt": endDate }
}},
{ "$group": {
"_id": {
"year": { "$year": "$time" },
"dayOfYear": { "$dayOfYear": "$time" },
"hour": { "$hour": "$time" },
"minute": {
"$subtract": [
{ "$minute": "$time" },
{ "$mod": [ { "$minute": "$time" }, 10 ] }
]
}
},
"count": { "$sum": 1 }
}}
]).forEach(function(doc) {
doc._id = new Date(doc._id);
printjson(doc);
})
然后你有你的区间分组输出与真正的Date
对象。
像这样?
pipeline = [
{"$project":
{"date": {
"year": {"$year": "$time"},
"month": {"$month": "$time"},
"day": {"$dayOfMonth": "$time"},
"hour": {"$hour": "$time"},
"minute": {"$subtract": [
{"$minute": "$time"},
{"$mod": [{"$minute": "$time"}, 10]}
]}
}}
},
{"$group": {"_id": "$date", "count": {"$sum": 1}}}
]
的例子:
> db.foo.insert({"time": new Date(2015, 7, 21, 22, 21)})
> db.foo.insert({"time": new Date(2015, 7, 21, 22, 23)})
> db.foo.insert({"time": new Date(2015, 7, 21, 22, 45)})
> db.foo.insert({"time": new Date(2015, 7, 21, 22, 33)})
> db.foo.aggregate(pipeline)
和输出:
{ "_id" : { "year" : 2015, "month" : 8, "day" : 21, "hour" : 20, "minute" : 40 }, "count" : 1 }
{ "_id" : { "year" : 2015, "month" : 8, "day" : 21, "hour" : 20, "minute" : 20 }, "count" : 2 }
{ "_id" : { "year" : 2015, "month" : 8, "day" : 21, "hour" : 20, "minute" : 30 }, "count" : 1 }
用指针代替具体的答案。使用日期聚合,您可以很容易地对分钟、小时和给定的时间段执行此操作。每隔10分钟就会有点棘手,但可能会有一些争吵。然而,对于大型数据集,聚合将慢得像坚果一样。
我建议提取插入后的分钟数
{
"_id" : ObjectId("55ae6ea558a5d3fe018b4568"),
"request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3",
"time" : ISODate("2015-07-21T16:00:00.00Z"),
"minutes": 16
}
尽管添加四分位数和六分位数或任何N可能听起来非常荒谬
{
"_id" : ObjectId("55ae6ea558a5d3fe018b4568"),
"request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3",
"time" : ISODate("2015-07-21T16:00:00.00Z"),
"minutes": 16,
"quartile: 1,
"sextile: 2,
}
首先尝试在会议记录上添加$div。不做天花板和地板。但是看看
在Mongodb聚合框架中是否有floor function ?