Получить последний вставленный элемент в день

Есть ли в mongodb функция, которую я могу использовать для получения последнего вставленного элемента в день? У меня есть коллекция, в которой мне нужно получить последний вставленный элемент в день, данные сгруппированы на почасовой основе, как в приведенной ниже структуре.

{
   timestamp: 2017-05-04T09:00:00.000+0000,
   data: {}
},
{
   timestamp: 2017-05-04T10:00:00.000+0000,
   data: {}
}

Я думал об использовании проекции, но я не совсем уверен, как я мог бы это сделать.

Изменить: Кроме того, поскольку mongodb хранит данные в формате UTC, я также хотел бы учитывать смещение.


person Bazinga777    schedule 06.06.2017    source источник
comment
сначала отсортируйте данные по метке времени (от самой старой к самой новой). Затем сгруппируйте документ по дням (на этапе $group) и сохраните последний data с $last.   -  person felix    schedule 06.06.2017


Ответы (1)


Вы можете $sort использовать $last для элемента с округлением ключа группировки до каждого дня:

db.collection.aggregate([
  { "$sort": { "timestamp": 1 } },
  { "$group": {
     "_id": {
       "$add": [
         { "$subtract": [
           { "$subtract": [ "$timestamp", new Date(0) ] },
           { "$mod": [
             { "$subtract": [ "$timestamp", new Date(0) ] },
             1000 * 60 * 60 * 24
           ]}
         ]},
         new Date(0)
       ]
     },
     "lastDoc": { "$last": "$$ROOT" }
  }}
])

Таким образом, сортировка заставляет вещи отображаться по порядку, а затем группировка _id округляется для каждого дня с помощью некоторой математики даты. Вы вычитаете дату эпохи из текущей даты, чтобы сделать ее числом. Используйте модуль для округления до дня, затем добавьте дату эпохи к числу, чтобы получить Date.

Итак, пройдясь по математике, мы получили значение метки времени из даты с помощью строки $subract. Делаем это пару раз:

{ "$subtract": [ "$timestamp", new Date(0) ] }

// Is roughly internally like
ISODate("2017-06-06T10:44:37.627Z") - ISODate("1970-01-01T00:00:00Z")
1496745877627

Затем есть модуль с $mod, который при применении к числовому значению возвращает разницу. 1000 миллисекунд * 60 секунд * 60 * минут * 24 часа дают другой аргумент:

 { "$mod": [
   { "$subtract": [ "$timestamp", new Date(0) ] },
   1000 * 60 * 60 * 24
 ]}

 // Equivalent to 
 1496745877627 % (1000 * 60 * 60 * 24)
 38677627

Затем идет упаковка $subtract двух чисел:

 { "$subtract": [
   { "$subtract": [ "$timestamp", new Date(0) ] },
   { "$mod": [
     { "$subtract": [ "$timestamp", new Date(0) ] },
     1000 * 60 * 60 * 24
   ]}
 ]}

 // Subtract "difference" of the modulo to a day
 // from the milliseconds value of the current date
 1496745877627 - 38677627

 1496707200000

Затем добавьте обратно значение даты эпохи, чтобы создать дату, округленную до текущего дня, что для конвейера агрегации в основном выглядит как предоставление значения миллисекунды конструктору:

new Date(1496707200000)
ISODate("2017-06-06T00:00:00Z")

Который принимает значение метки времени и вычитает разницу делителя из «одного дня» и заканчивается временем в «начале дня».

Просто используйте здесь $$ROOT для представления всего документа. Но любой путь к документу, указанный здесь для $last, дает результат.

person Neil Lunn    schedule 06.06.2017
comment
Спасибо, но не могли бы вы немного подробнее объяснить процесс группировки? Это сработало, кстати, после того, как я заменил все числа одним числом - person Bazinga777; 06.06.2017
comment
@ Bazinga777 На самом деле это не должно иметь значения. У меня отлично работает в JavaScript, и я делаю это все время. Это то, как работает математика, чтобы округлить это до дня, который вам нужно понять лучше, чем текущее объяснение? - person Neil Lunn; 06.06.2017
comment
Дело в том, что мне нужно добавить смещение для каждого часового пояса. Я пропустил это в своем вопросе - person Bazinga777; 06.06.2017
comment
точно, я хочу знать математику - person Bazinga777; 06.06.2017
comment
@ Bazinga777 Хорошо. Это настолько многословно, насколько я могу это сделать, чтобы это не звучало как лекция. Надеюсь, ты чему-нибудь научишься. Таким образом, ваше смещение часового пояса должно идти туда, где вы конвертируете текущую метку времени из типа Date BSON в числовой тип. Поэтому в тех же строках, где используются повторяющиеся операторы $subtract. Делайте так же все время. - person Neil Lunn; 06.06.2017
comment
Спасибо тебе большое за это. Я даже понял, как теперь учитывать смещение - person Bazinga777; 06.06.2017
comment
@ Bazinga777 Хорошо для тебя. На самом деле у меня есть код, который также учитывает изменения в переходе на летнее время и создает различные вычисления с помощью $cond с учетом диапазона дат, который где-то пересекает изменение. Я думаю, что здесь есть ответ, где я это показал, но, похоже, не могу его найти. В любом случае, по крайней мере, у вас есть то, что вы хотите на данный момент. - person Neil Lunn; 06.06.2017
comment
Буду признателен за любые ресурсы - person Bazinga777; 06.06.2017