【问题标题】:Count of Events within a MongoDB Collection by Day within a Date Range日期范围内按天计算的 MongoDB 集合中的事件计数
【发布时间】:2014-07-24 09:51:08
【问题描述】:

我试图弄清楚如何在 MongoDB 集合中按指定日期范围内发生的所有日期执行事件计数。

MongoDB 中的示例文档架构:

{
    eventNum: 1234,
    startDate:ISODate("2014-01-01"),
    endDate: ISODate("2014-01-04")
    eventType: System Crash
}
{
    eventNum: 4567,
    startDate: ISODate("2014-01-04"),
    endDate: ISODate("2014-01-05")
    eventType: Maintenance
}

我想要了解的是日期范围:

1/1/2014 - 1/6/2014

此范围内每天的事件总数是多少?

结果是:

1/1/2014: 1
1/2/2014: 1
1/3/2014: 1
1/4/2014: 2
1/5/2014: 1
1/6/2014: 0

我的问题实际上在于跟踪的数据不是基于单独的一天,这将允许我按日期执行基本聚合。事件有开始和结束日期。

【问题讨论】:

  • 那些“日期”对我来说就像字符串,所以这对你没有帮助。您需要将字符串修复为实际的“日期”类型,因为它们不会以词法方式进行比较。
  • 好的,我只是想模拟这个例子。假设它们是有效日期,您将如何进行这种类型的聚合?

标签: mongodb mapreduce aggregation-framework


【解决方案1】:

不幸的是,更多的是mapReduce 问题,否则您将无法发出完成这项工作所需的日期。

db.events.mapReduce(
    function() {
        var oneDay = ( 1000 * 60 * 60 * 24 ),
            start = this.startDate.valueOf()
            - ( this.startDate.valueOf() % oneDay ),
            end = ((this.endDate.valueOf()
            - ( this.endDate.valueOf() % oneDay )) + oneDay);

        for ( var day = start; day < end; day += oneDay ) {
           emit ( new Date( day ), 1 );
        }
    },
    function(key, values) {
        return Array.sum( values );
    },
    { 
         "query": { 
             "startDate": { "$gte": new Date("2014-01-01") },
             "endDate": { "$lt": new Date("2014-01-06") }
          },
         "out": { "inline": 1 }
    }
)

如果您真的想要日期范围内不存在的事件的零值,您可以稍作更改:

db.events.mapReduce(
    function() {
        var oneDay = ( 1000 * 60 * 60 * 24 ),
            start = this.startDate.valueOf()
            - ( this.startDate.valueOf() % oneDay ),
            end = ((this.endDate.valueOf()
            - ( this.endDate.valueOf() % oneDay )) + oneDay);

        for ( var day = start; day < end; day += oneDay ) {
           emit ( new Date( day ), 1 );
        }

        for ( var day = end; day <= ending.valueOf(); day += oneDay ) {
           emit( new Date( day ), 0 );
        }

    },
    function(key, values) {
        return Array.sum( values );
    },
    { 
         "query": { 
             "startDate": { "$gte": new Date("2014-01-01") },
             "endDate": { "$lt": new Date("2014-01-06") }
          },
          "scope": { "ending": new Date("2014-01-06") },
          "out": { "inline": 1 }
    }
)

这会给你想要的输出:

   "results" : [
            {
                    "_id" : ISODate("2014-01-01T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-02T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-03T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-04T00:00:00Z"),
                    "value" : 2
            },
            {
                    "_id" : ISODate("2014-01-05T00:00:00Z"),
                    "value" : 1
            },
            {
                    "_id" : ISODate("2014-01-06T00:00:00Z"),
                    "value" : 0
            }
]

【讨论】:

  • 谢谢。我希望有一些方法可以使用聚合框架来做到这一点:(
  • @FarscapePROJ 遗憾的是,根据我在答案中的最初陈述,没有。没有可用的方法允许您将 startDate 和 endDate 值之间的日期作为单独的文档发出。这将需要某种“for loop”类型的运算符的实现,如这里的 mapReduce 方法所示。但这还不存在。
  • @NeilLunn,在过去的几年里,mongo 发生了很多变化。你知道这个答案是否仍然成立吗?这仍然是唯一的方法吗?
猜你喜欢
  • 2011-02-05
  • 1970-01-01
  • 2019-04-12
  • 1970-01-01
  • 2018-12-15
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多