计算日期在日期范围内的记录数量?

时间:2016-02-08 17:58:03

标签: mongodb mapreduce mongodb-query aggregation-framework

我有一个包含以下文档的集合:

{startDate: ISODate("2016-01-02T00:00:00Z"), endDate: ISODate("2016-01-05T00:00:00Z")},
{startDate: ISODate("2016-01-02T00:00:00Z"), endDate: ISODate("2016-01-08T00:00:00Z")},
{startDate: ISODate("2016-01-05T00:00:00Z"), endDate: ISODate("2016-01-08T00:00:00Z")},
{startDate: ISODate("2016-01-05T00:00:00Z"), endDate: ISODate("2016-01-10T00:00:00Z")},
{startDate: ISODate("2016-01-07T00:00:00Z"), endDate: ISODate("2016-01-10T00:00:00Z")}

我想为最低startDate和最高endDate之间的每个日期返回一条记录。与每条记录一起,我想返回startDateendDate包含此日期的记录数的计数。

因此,对于我的上述示例,分钟startDate1/2/2016,最大endDate1/10/2016,因此我希望返回这两者之间的所有日期以及计数。请参阅以下所需的输出:

{date: ISODate("2016-01-02T00:00:00Z"), count: 2}
{date: ISODate("2016-01-03T00:00:00Z"), count: 2}
{date: ISODate("2016-01-04T00:00:00Z"), count: 2}
{date: ISODate("2016-01-05T00:00:00Z"), count: 4}
{date: ISODate("2016-01-06T00:00:00Z"), count: 3}
{date: ISODate("2016-01-07T00:00:00Z"), count: 4}
{date: ISODate("2016-01-08T00:00:00Z"), count: 4}
{date: ISODate("2016-01-09T00:00:00Z"), count: 2}
{date: ISODate("2016-01-010T00:00:00Z"), count: 2}

如果这没有意义,请告诉我,我可以尝试更详细地解释。

我可以使用如下循环来完成此操作:

var startDate = ISODate("2016-01-02T00:00:00Z")
var endDate = ISODate("2016-02-10T00:00:00Z")
while(startDate < endDate){
  var counts = db.data.find(
      {
        startDate: {$lte: startDate},
        endDate: {$gte: startDate}
      }
    ).count()
  print(startDate, counts)
  startDate.setDate(startDate.getDate() + 1)
}

但我想知道是否有办法使用聚合框架来做到这一点?我来自一个主要是SQL的背景,循环获取数据通常是一个坏主意。这个规则适用于MongoDB吗?我是否应该关注在这里使用循环并尝试使用聚合框架,或者这是一个有效的解决方案吗?

2 个答案:

答案 0 :(得分:1)

这里最好的选择是mapReduce。这是因为您可以在每个文档中的“startDate”和“endDate”之间循环值,并在这些值之间每天(或其他所需的间隔)发出。然后,只需从所有数据中累积每个发出的日期键:

db.collection.mapReduce(
    function() {
        for( var d = this.startDate.valueOf(); d <= this.endDate.valueOf(); d += 1000 * 60 * 60 * 24 ) {
            emit(new Date(d), 1)
        }
    },
    function(key,values) {
        return Array.sum(values);
    },
    { "out": { "inline": 1 } }
)

这会产生如下结果:

{
        "results" : [
                {
                        "_id" : ISODate("2016-01-02T00:00:00Z"),
                        "value" : 2
                },
                {
                        "_id" : ISODate("2016-01-03T00:00:00Z"),
                        "value" : 2
                },
                {
                        "_id" : ISODate("2016-01-04T00:00:00Z"),
                        "value" : 2
                },
                {
                        "_id" : ISODate("2016-01-05T00:00:00Z"),
                        "value" : 4
                },
                {
                        "_id" : ISODate("2016-01-06T00:00:00Z"),
                        "value" : 3
                },
                {
                        "_id" : ISODate("2016-01-07T00:00:00Z"),
                        "value" : 4
                },
                {
                        "_id" : ISODate("2016-01-08T00:00:00Z"),
                        "value" : 4
                },
                {
                        "_id" : ISODate("2016-01-09T00:00:00Z"),
                        "value" : 2
                },
                {
                        "_id" : ISODate("2016-01-10T00:00:00Z"),
                        "value" : 2
                }
        ],
        "timeMillis" : 35,
        "counts" : {
                "input" : 5,
                "emit" : 25,
                "reduce" : 9,
                "output" : 9
        },
        "ok" : 1
}

您的日期四舍五入到样本中的某一天,但如果它们不在实际数据中,则只需要应用日期数学就可以实现每个时间间隔的舍入。

答案 1 :(得分:0)

在mongodb聚合框架中,有阶段而不是循环。它是一个管道,它经过每个阶段,直到达到指定的最后阶段。这就是为什么你在使用聚合框架时看到[]的原因。有几个阶段,仅举几例(匹配,组和项目)。看看他们的文档很简单。无论如何,这非常简短。至于你的问题,这是我的主张:

我没试过这个。如果您可以尝试这个并让我知道它是否有效:

首先,您只使用$ match保留日期在您想要的范围内。然后使用$ group阶段。 例如:

db.collection.aggregate{
    [
         {$match: {
             $and : [
                   {startDate: {$gte:ISODate("2016-01-02T00:00:00Z")}, 
                   {endDate: {$lte:ISODate("2016-02-10T00:00:00Z")}

                    ]
         },

         {$group:
             {_id: {startDate:"$startDate",endDate:"$endDate"},
              count:{$sum:1}
             }
          }
    ]
 }

如果您只想使用startDate进行分组,请按照示例替换

_id: {startDate:"$startDate",endDate:"$endDate"

用这个:

_id: "$startDate"

我希望有帮助