MongoDb 聚合:按日期分组

12

我正在尝试按时间戳对名为“foo”的集合进行分组 { _id,TimeStamp }

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

期望得到多个日期,但结果只有一个日期。我使用的数据是正确的(包含许多"foo"和不同的日期,除了1970年)。日期解析存在问题,但我还无法解决。

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

尝试了这个:

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} }
])

结果:

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

还有那个:

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} }
])

无法在结果中看到日期

{
    "result" : [ 
        {
            "count" : 412
        }, 
        {
            "count" : 1702
        }, 
        {
            "count" : 422
        }
    ],
    "ok" : 1
}

1
你不能在聚合中使用Javascript - 也就是说,你的新日期并不是在你试图传递给它的值上进行评估的。 - Asya Kamsky
如果您想将时间转换为日期(每日或每小时),请参阅此处的一些示例:http://www.kamsky.org/1/post/2013/03/stupid-date-tricks-with-aggregation-framework.html - Asya Kamsky
Date.UTC 是问题所在。我不认为在 MongoDB 中存在这样的构造。 - Salvador Dali
@AsyaKamsky,我喜欢你的博客:-)。你能把链接放到你的个人资料里吗? - Salvador Dali
1
从3.0版本开始,有一个dateToString格式化选项可以让生活更轻松。https://docs.mongodb.com/manual/reference/operator/aggregation/dateToString/#exp._S_dateToString - cyberwombat
3个回答

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

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

  1. $project 结合 $substr 从每个文档中获取 ISODate 对象的前10个字符(YYYY:MM:DD),结果是一个包含 "_id" 和 "day" 字段的文档集合;
  2. $group 按 day 分组,将匹配到的每个文档的值累加1;
  3. $sort 按 "_id" 升序排序,这是可选的,如果需要排序结果的话。

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


2
这可能回答了问题,但如果它解释了为什么它回答了问题,那么它将是一个更好的答案,并且对于每个人来说都更有用。仅提供一堆代码而没有任何解释并不是很有帮助。 - AdrianHHH

13
这取决于你是否想在最终输出中将日期作为ISODate类型。如果是的话,那么你可以做以下两件事之一:
1. 从你的时间戳中提取$year、$month和$dayOfMonth,并重新构建一个新的日期(你已经在尝试这样做了,但是你使用的语法在聚合框架中不起作用)。 2. 如果原始时间戳是ISODate()类型,则可以进行日期算术运算,从时间戳中减去小时、分钟、秒和毫秒,以获得“舍入”到天的新日期。
这里有一个example of 2 here
以下是如何执行第一种方法。我假设所有的日期都是今年,但你可以很容易地调整数学计算来适应你最旧的日期。
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]}
         ] } } };

示例数据:

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

聚合结果:
> db.foos.aggregate(project1, project2)
{ "tsDate" : ISODate("2013-11-13T00:00:00Z") }
{ "tsDate" : ISODate("2014-02-01T00:00:00Z") }

0

这是我在其中一个项目中使用的:

   collection.aggregate(
      // group results by date
      {$group : {
        _id : { date : "$date" }
        // do whatever you want here, like $push, $sum...
      }},

      // _id is the date
      {$sort : { _id : -1}},                        
      {$orderby: { _id : -1 }})
    .toArray()

在Mongo中,$date是一个日期对象。我得到的结果是按日期索引的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接