2015-07-21 151 views
3

我有存储在蒙戈集合,其包括基本信息作为REQUEST_ID和将其加入到集合中的时间,例如一些日志数据:蒙戈聚合

{ 
    "_id" : ObjectId("55ae6ea558a5d3fe018b4568"), 
    "request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3", 
    "time" : ISODate("2015-07-21T16:00:00.00Z") 
} 

我想知道是否我可以使用聚合框架来聚合一些统计数据。我希望获得在最近X小时的每个N分钟间隔内创建的对象的计数。

所以,我需要10周分钟的时间间隔过去1小时输出应该是这样的:

{ "_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 } 

我会用它来获取数据的图表。

任何意见是赞赏!

回答

1

这样的事情?

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 } 
0

代替具体的答案的一个指针。你可以很容易地使用date aggregations分钟,几小时和给定的时间。每10分钟会有点棘手,但可能有一些争论。尽管如此,由于大型数据集的坚固性,聚合速度会很慢。

我建议提取分钟后插入

{ 
    "_id" : ObjectId("55ae6ea558a5d3fe018b4568"), 
    "request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3", 
    "time" : ISODate("2015-07-21T16:00:00.00Z"), 
    "minutes": 16 
} 

即使它听起来荒谬绝伦增加位数和sextiles或任何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。不做细胞和地板。但检查出

Is there a floor function in Mongodb aggregation framework?

+0

['$ mod'](http://docs.mongodb.org/manual/reference/operator/aggregation/mod/)运算符很容易做到这一点。 –

1

有一对夫妇的接近这取决于其输出格式最适合您需要的方法。主要的注意事项是,使用"aggregation framework"本身,您实际上不能返回“投射”日期,但可以在处理API中的结果时获取易于重构为对象的值。

第一种方法是使用现有的"Date Aggregation Operators"到聚合框架:

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计算实际日期。

或者您可以使用普通的“日期数学”来获得自“时代”以来可以再次被直接馈送到日期构造器的毫秒数。

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,如图所示。因为JavaScript函数实际上允许重铸为日期,但比聚合框架要慢,当然也可以不用,但是,如果你真的“纯”了一个Date对象而没有后处理,那么你总是可以使用"mapReduce"。光标响应:

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对象分组。

+0

在实际项目之前使用组的好技巧,很高兴知道如果在组之前使用了所有文档,项目正在循环。我希望在这里实现的一件事(我更新了我的问题)实际上是在空间间隔(不包含任何对象的间隔)结果中计数为0,因为我将这些传递给图框架。这可以节省解析的时间,并在使用数据的实际代码中添加该时间。这是可以实现的吗? –

+0

@JeromeWalters真的不是明智的做法。如果你仔细想想,你只能对“存在”的键进行“求和”。所以尽管在范围内投入每个“可能的”键是“可能的”,但它实际上并不实用,因为您正在抛出一个包含所有这些键的变量。聪明的钱是在你的API代码中将“结合”到空白的预期范围,而不是在服务器上“扔”所有这些值。同样适用于同一事物的SQL变体。只因为你“可以”并不意味着你“应该”。 –

+0

@JeromeWalters也“改变你的问题”,所以它与你问的内容本质上是“不同的”。这里收到的不好。如果一个答案引导你另一个问题,那么改为“问另一个问题”。 –