Группа агрегации MongoDb по дате

Я пытаюсь группировать по метке времени для коллекции с именем "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

Это зависит от того, хотите ли вы иметь дату как тип ISODate в конечном выпуске. Если да, то вы можете сделать одну из двух вещей:

  • Извлеките $year, $month, $dayOfMonth из вашей метки времени и затем восстановите новую дату из них (вы уже пытаетесь это сделать, но используете синтаксис, который не работает в структуре агрегации).

  • Если исходная временная метка имеет тип ISODate(), вы можете выполнить арифметику дат, чтобы вычесть часы, минуты, секунды и миллисекунды из метки времени, чтобы получить новую дату, "округленную" до дня.

Здесь пример 2 здесь.

Вот как вы это сделаете 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]}
         ] } } };

Пример данных:

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

Ответ 2

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

Групповая дата может быть выполнена в два этапа в структуре агрегации, для сортировки результата необходим дополнительный третий шаг, если требуется сортировка:

  • $project в сочетании с $substr принимает первые 10 символов (YYYY: MM: DD) объекта ISODate из каждого документа (результатом является сбор документов с полями "_id" и "day" );
  • $group группируются по дням, добавляя (суммируя) номер 1 для каждого соответствующего документа;
  • $sort возрастающий на "_id", который является днем ​​предыдущего шага агрегации - это необязательно, если желаемый отсортированный результат.

Это решение не может воспользоваться такими индексами, как db.twitter.ensureIndex( { TimeStamp: 1 } ), поскольку оно преобразует объект ISODate в строковый объект "на лету". Для больших коллекций (миллионы документов) это может быть узким местом производительности, и следует использовать более сложные подходы.

Ответ 3

Это то, что я использую в одном из моих проектов:

   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()

Где $date - объект Date в монго. Я получаю результаты, индексированные по дате.