2015-01-03 2 views
3

У меня есть коллекция с DateTime поля и графа поданную:MongoDB группы по дате интервалов

{ 
    _id: null, 
    datetime: new Date(), 
    count: 1234 
} 

Я хочу, чтобы получить суммы от подсчета 24 часа, 7 дней и 30 дней с интервалом. Результат должен быть, как:

{"sum": 100, "interval": "day"}, 
{"sum": 700, "interval": "week"}, 
{"sum": 3000, "interval": "month"} 

РЕДАКТИРОВАТЬ:

Более аннотация: мне нужно результаты группы от нескольких условий (в данном случае - несколько временных интервалов)

MySQL Пример:

SELECT 
    IF (time>CURRENT_TIMESTAMP() - INTERVAL 24 HOUR, 1, 0) last_day, 
    IF (time>CURRENT_TIMESTAMP() - INTERVAL 168 HOUR, 1, 0) last_week, 
    IF (time>CURRENT_TIMESTAMP() - INTERVAL 720 HOUR, 1, 0) last_month, 
    SUM(count) count 
FROM table 
GROUP BY last_day, 
      last_week, 
      last_month 

ответ

3

Существует два разных способа сделать это. Один из них заключается в выпуске отдельного запроса count() для каждого из диапазонов. Это довольно просто, и если поле datetime проиндексировано, оно будет быстрым.

Второй способ состоит в объединении их всех в один запрос с использованием аналогичного метода, например, вашего SQL-примера. Для этого вам необходимо использовать метод aggregate(), создав конвейер $project, чтобы создать значения 0 или 1 для новых полей «last_day», «last_week» и «last_month», а затем использовать оператор $group для выполнения суммы.

17

Есть date aggregation operators, доступный для структуры агрегации MongoDB. Так, например, $dayOfYear оператор используется, чтобы получить это значение с момента для использования в группировке:

db.collection.aggregate([ 
    { "$group": { 
     "_id": { "$dayOfYear": "$datetime" }, 
     "total": { "$sum": "$count" } 
    }} 
]) 

Или вы можете использовать вместо даты математики подход. Применяя дату эпохальных вы преобразовать объект даты в номер, где может быть применена по математике:

db.collection.aggregate([ 
    { "$group": { 
     "_id": { 
      "$subtract": [ 
       { "$subtract": [ "$datetime", new Date("1970-01-01") ] }, 
       { "$mod": [ 
        { "$subtract": [ "$datetime", new Date("1970-01-01") ] }, 
        1000 * 60 * 60 * 24 
       ]} 
      ] 
     }, 
     "total": { "$sum": "$count" } 
    }} 
]) 

Если то, что вы после этих интервалы от текущего момента времени то, что вы хотите в основном дата математика подход и работает в некоторых условных через $cond оператора:

db.collection.aggregate([ 
    { "$match": { 
     "datetime": { 
      "$gte": new Date(new Date().valueOf() - (1000 * 60 * 60 * 24 * 365)) 
     } 
    }}, 
    { "$group": { 
     "_id": null, 
     "24hours": { 
      "$sum": { 
       "$cond": [ 
        { "$gt": [ 
         { "$subtract": [ "$datetime", new Date("1970-01-01") ] }, 
         new Date().valueOf() - (1000 * 60 * 60 * 24) 
        ]}, 
        "$count", 
        0 
       ] 
      } 
     }, 
     "30days": { 
      "$sum": { 
       "$cond": [ 
        { "$gt": [ 
         { "$subtract": [ "$datetime", new Date("1970-01-01") ] }, 
         new Date().valueOf() - (1000 * 60 * 60 * 24 * 30) 
        ]}, 
        "$count", 
        0 
       ] 
      } 
     }, 
     "OneYear": { 
      "$sum": { 
       "$cond": [ 
        { "$gt": [ 
         { "$subtract": [ "$datetime", new Date("1970-01-01") ] }, 
         new Date().valueOf() - (1000 * 60 * 60 * 24 * 365) 
        ]}, 
        "$count", 
        0 
       ] 
      } 
     } 
    }} 
]) 

Это по сути тот же подход, как пример SQL, где запрос условно оценивает, попадет ли значение даты в требуемом диапазоне и решает, следует ли добавьте v в сумме.

Одно дополнение здесь - дополнительный этап $match, чтобы ограничить запрос действиями только на те элементы, которые, возможно, будут находиться в пределах максимального диапазона в течение одного года, о котором вы просите. Это делает его немного лучше, чем представленный SQL, поскольку индекс может использоваться для фильтрации этих значений, и вам не нужно «перебирать силу» через несовпадающие данные в коллекции.

Всегда рекомендуется ограничивать ввод $match при использовании конвейера агрегации.

+0

Мне не нужны суммы каждый день. Мне нужны суммы только этих трех временных интервалов. – jonasasx

+0

@jonasasx Тогда что, по-вашему, вы делаете? Что вы ожидаете?Все суммы в одном результате запроса или выполняются отдельные запросы в порядке? Вы должны объяснить, что вы ожидаете в результате. –

+0

Я хочу получить три записи за каждый интервал (день, неделя, месяц). В идеале, в одном запросе. – jonasasx