2017-04-24 108 views
1

如何将MongoDB 3.4.1中的文档与代表日期范围的两个字段分隔到该范围所涵盖的日期中?MongoDB聚合方面在日期范围内按日期分段

例如,我有以下文件:

{ start: ISODate('2017-04-01T12:30:00Z'), end: ISODate('2017-04-02T12:30:00Z') }, 
{ start: ISODate('2017-04-04T12:00:00Z'), end: ISODate('2017-04-04T12:30:00Z') }, 
{ start: ISODate('2017-04-04T12:30:00Z'), end: ISODate('2017-04-08T12:30:00Z') }, 
{ start: ISODate('2017-04-05T12:30:00Z'), end: ISODate('2017-04-06T12:30:00Z') }, 
{ start: ISODate('2017-04-08T12:30:00Z'), end: ISODate('2017-04-09T12:30:00Z') } 

,我期待到斗那些成以下柱状图:

[ 
    { '2017-04-01': 1 }, 
    { '2017-04-02': 1 }, 
    { '2017-04-04': 2 }, 
    { '2017-04-05': 2 }, 
    { '2017-04-06': 2 }, 
    { '2017-04-07': 1 }, 
    { '2017-04-08': 2 }, 
    { '2017-04-09': 1 } 
] 

如果文档的日期范围涵盖的任何部分当天,该文件应计入当天的1个项目。

如果可能的话,我想用单个聚合管道执行此分流。这是个人时间戳字段非常简单的用了一天格式化突出他们如:

{ $project: { day: { $dateToString: { format: '%Y-%m-%d', date: '$start' } } } } 

,但它似乎并不为那些有支持此一日期范围内聚集的运营商。

+0

使用map-reduce可以很容易地完成,但速度不会太好。 –

+0

@AlexBlex谢谢您的建议,因为这确实需要澄清一点:我正在寻求在现有聚合管道中执行此分档。 – brianghig

回答

2

不是很简单,但这工作。如果你想要确切的输出格式,你需要在3.4.4中引入新的$objectToArray表达式。

编辑:这不考虑闰年,因为它假设每年有365天。

const millisPerDay = 1000 * 60 * 60 * 24; 

var results = db.dates.aggregate([ 
    { 
     $project: { 
      start: 1, 
      range: { 
       $range: [ 
        0, 
        { 
        $add: [ 
         1, 
         { 
          $add: [ 
           {$multiply: [365, {$subtract: [{$year: "$end"}, {$year: "$start"}]}]}, 
           {$subtract: [{$dayOfYear: "$end"}, {$dayOfYear: "$start"}]} 
          ] 
         } 
        ] 
        } 
       ] 
      } 
     } 
    }, 
    { 
     $project: { 
      daysOfEvent: { 
       $map: { 
        input: "$range", 
        in : {$add: ["$start", {$multiply: ["$$this", millisPerDay]}]} 
       } 
      } 
     } 
    }, 
    {$unwind: "$daysOfEvent"}, 
    {$group: {_id: {$dateToString: {format: '%Y-%m-%d', date: "$daysOfEvent"}}, count: {$sum: 1}}}, 
    {$sort: {_id: -1}}, 
    {$replaceRoot: {newRoot: {$arrayToObject: [[{k: "$_id", v: "$count"}]]}}} 
]); 
+0

感谢您评论良好的代码@Charlie。我看到一些意外的错误,导致从投影中计算出非常长的日期范围。例如: {“start”:ISODate(“2016-01-26T05:00:00Z”),“end”:ISODate(“2016-07-04T04:00:00Z”)} 结果日期范围出去到2022年。 – brianghig

+0

哎呀!更新。看起来像我的逻辑来计算两个日期之间的天数关闭。我也意识到这并不代表闰年。 –