MongoDb聚合按日期分组

jac*_*kie 12 mongodb aggregation-framework

我正在尝试按时间戳分组名为"foo"的集合{_id,TimeStamp}

db.foos.aggregate(
[
   {$group : { _id : new Date (Date.UTC({ $year : '$TimeStamp' },{ $month : '$TimeStamp' },{$dayOfMonth : '$TimeStamp'}))       }}
])
Run Code Online (Sandbox Code Playgroud)

期待许多日期,但结果只是一个日期.我正在使用的数据是正确的(除了1970年以外有很多foo和不同的日期).解析日期有一些问题,但我还无法解决.

{
    "result" : [ 
        {
            "_id" : ISODate("1970-01-01T00:00:00.000Z")
        }
    ],
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)

试过这一个:

db.foos.aggregate(
[
   {$group : { _id : { year : { $year : '$TimeStamp' }, month : { $month : '$TimeStamp' }, day : {$dayOfMonth : '$TimeStamp'} }, count : { $sum : 1 }       }},
   {$project : { parsedDate : new Date('$_id.year', '$_id.month', '$_id.day') , count : 1, _id : 0} }
])
Run Code Online (Sandbox Code Playgroud)

结果:

uncaught exception: aggregate failed: {
    "errmsg" : "exception: disallowed field type Date in object expression (at 'parsedDate')",
    "code" : 15992,
    "ok" : 0
}
Run Code Online (Sandbox Code Playgroud)

而那一个:

db.foos.aggregate(
[
   {$group : { _id : { year : { $year : '$TimeStamp' }, month : { $month : '$TimeStamp' }, day : {$dayOfMonth : '$TimeStamp'} }, count : { $sum : 1 }       }},
   {$project : { parsedDate : Date.UTC('$_id.year', '$_id.month', '$_id.day') , count : 1, _id : 0} }
])
Run Code Online (Sandbox Code Playgroud)

无法在结果中看到日期

{
    "result" : [ 
        {
            "count" : 412
        }, 
        {
            "count" : 1702
        }, 
        {
            "count" : 422
        }
    ],
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)

pet*_*tra 18

db.foos.aggregate(
    [   
        {   $project : { day : {$substr: ["$TimeStamp", 0, 10] }}},        
        {   $group   : { _id : "$day",  number : { $sum : 1 }}},
        {   $sort    : { _id : 1 }}        
    ]
)
Run Code Online (Sandbox Code Playgroud)

按日期分组可以在聚合框架中以两个步骤完成,如果需要排序,则需要另外的第三步来对结果进行排序:

  1. $project结合使用$substr来自每个文档的ISODate对象的前10个字符(YYYY:MM:DD)(结果是具有字段"_id"和"day"的文档的集合);
  2. $group 按天分组,为每个匹配的文件添加(求和)数字1;
  3. $sort 通过"_id"升序,这是前一个聚合步骤的日期 - 如果需要排序结果,这是可选的.

此解决方案无法利用索引db.twitter.ensureIndex( { TimeStamp: 1 } ),因为它可以动态地将ISODate对象转换为字符串对象.对于大型集合(数百万个文档),这可能是性能瓶颈,应该使用更复杂的方法.

  • 这可能会或可能不会回答这个问题,但如果它解释了为什么它回答了这个问题,它将是一个更好的答案,对每个人都更有用.只提供一堆代码而不做任何解释并不是很有帮助. (2认同)

Asy*_*sky 12

这取决于您是否希望在最终输出中将日期作为ISODate类型.如果是这样,那么你可以做以下两件事之一:

  1. 提取$year,$month,$dayOfMonth从您的时间戳,然后重建一个新的日期从他们的(你已经尝试这样做,但你使用的语法并不在聚合框架工作).

  2. 如果原始时间戳是ISODate()类型,那么您可以执行日期算术以从时间戳中减去小时,分钟,秒和毫秒,以获得"舍入"到该日期的新日期.

这里有一个例子.

您将如何做1.我假设您的所有日期都是今年,但您可以轻松调整数学以适应您最早的日期.

project1={$project:{_id:0, 
                   y:{$subtract:[{$year:"$TimeStamp"}, 2013]},
                   d:{$subtract:[{$dayOfYear:"$TimeStamp"},1]}, 
                   TimeStamp:1, 
                   jan1:{$literal:new ISODate("2013-01-01T00:00:00")}
         } };
project2={$project:{tsDate:{$add:[
                       "$jan1",
                       {$multiply:["$y", 365*24*60*60*1000]},
                       {$multiply:["$d", 24*60*60*1000]}
         ] } } };
Run Code Online (Sandbox Code Playgroud)

样本数据:

db.foos.find({},{_id:0,TimeStamp:1})
{ "TimeStamp" : ISODate("2013-11-13T19:15:05.600Z") }
{ "TimeStamp" : ISODate("2014-02-01T10:00:00Z") }
Run Code Online (Sandbox Code Playgroud)

聚合结果:

> db.foos.aggregate(project1, project2)
{ "tsDate" : ISODate("2013-11-13T00:00:00Z") }
{ "tsDate" : ISODate("2014-02-01T00:00:00Z") }
Run Code Online (Sandbox Code Playgroud)