MongoDB - разумно ли использовать агрегирование вместо обычного запроса диапазона дат? - PullRequest
0 голосов
/ 10 сентября 2018

Я пытаюсь выполнить запрос диапазона дат в MongoDB. Допустим, я хочу найти записи, которые были созданы в мае 2018 года. Теперь, согласно знаниям, мы можем достичь этого следующими способами.

  1. Один из них - выполнить простой запрос диапазона для созданного. Ex. {createdAt: { $gte: ISODate('2018-05-01'), $lte: ISODate('2018-05-31')}}

  2. Другим способом является агрегация db.collection.aggregate([{ $project: { year: { $year: '$createdAt' }, month: { $month: '$createdAt' }}, { $match: { year: 2018, month: 5}}])

Итак, когда использовать агрегацию, если мы можем достичь результата простым запросом? Пожалуйста, внесите исправления, если вышеуказанный запрос или подход неверен или устарел. Я проверяю это в Компасе.

Спасибо

Ответы [ 3 ]

0 голосов
/ 10 сентября 2018

Если вы можете сделать это с помощью простого запроса, просто оставьте его простым. Существует множество платформ, которые легко поддерживают запросы, но не агрегируют.

Агрегации сложнее писать и поддерживать. В некоторых случаях они вам действительно нужны (например, при работе с массивами или вложенными документами), но если нет, сохраните их с помощью find (). Пример того, когда нужны агрегации:

MongoDB - Получить последнее ненулевое значение поля из документов с отметкой времени

Кроме того, я не уверен, что ваш код агрегации работает так же, как код find (). Если вы хорошо проверите, сначала вы используете этап $ project, а затем вы подходите. Это означает, что вы проецируете все объекты БД и затем сопоставляете их, потому что вы не начали агрегацию с $ match. Это может быть очень медленно, если у вас много объектов.

Кроме того, $ match использует индексы только на первом этапе:

Поместите $ match как можно раньше в конвейер агрегации. Поскольку $ match ограничивает общее количество документов в агрегации конвейер, более ранние операции $ match минимизируют объем обработки вниз по трубе. Если вы поместите $ match в самом начале конвейер, запрос может использовать индексы, как и любой другой db.collection.find () или db.collection.findOne ().

0 голосов
/ 10 сентября 2018

По моему опыту, очень часто вскоре после того, как вы выполняете базовый find(), ваши потребности меняются и требуют мощности конвейера. Например, рассмотрим следующие документы:

{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 3 }
{ "_id" : 4, "date" : ISODate("2018-01-20T00:00:00Z") }
{ "_id" : 5 }
{ "_id" : 6 }
{ "_id" : 7, "date" : ISODate("2018-01-18T00:00:00Z") }
{ "_id"  :8, "date" : ISODate("2018-01-10T00:00:00Z") }

Мы хотим найти все, где дата <= 2018-01-15: </p>

db.foo.aggregate([
{$match: {"date": {$lte: new ISODate("2018-01-15")}} }
                ]);

{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }

по электронной почте Ой! Мы тоже хотим пустые даты:

db.foo.aggregate([
{$match: {"$or": [ {"date": {$lte: new ISODate("2018-01-15")}}, {"date": {$exists: false}} ] }}
]);

{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 3 }
{ "_id" : 5 }
{ "_id" : 6 }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }

И мы хотим, чтобы это было отсортировано:

db.foo.aggregate([
{$match: {"$or": [ {"date": {$lte: new ISODate("2018-01-15")}}, {"date": {$exists: false}} ] }}
,{$sort: {"date":1}}
]);

{ "_id" : 3 }
{ "_id" : 5 }
{ "_id" : 6 }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }

Хм. Но мы хотим, чтобы пробелы появлялись в конце отсортированного списка. Таким образом, мы перезаписываем поле date самим собой, или, если оно равно нулю, действительно далекую дату, которая теперь дает нам искомую последовательность:

db.foo.aggregate([
{$match: {"$or": [ {"date": {$lte: new ISODate("2018-01-15")}}, {"date": {$exists: false}} ] }}
,{$addFields: {"date": {$ifNull: [ "$date", new ISODate("3000-01-01")] }}}
,{$sort: {"date":1}}
]);

{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 3, "date" : ISODate("3000-01-01T00:00:00Z") }
{ "_id" : 5, "date" : ISODate("3000-01-01T00:00:00Z") }
{ "_id" : 6, "date" : ISODate("3000-01-01T00:00:00Z") }

Существует несколько вариантов этой темы, например, выполнение начального $project для создания общего ненулевого поля сортировки, но, поместив $match сразу, мы можем воспользоваться индексами, если они существуют. Истинная сила конвейера agg проявляется, когда в ваших документах есть массив данных, требующий запросов и манипуляций.

0 голосов
/ 10 сентября 2018

Вам не нужен этап $project (фактически, при написании вашей проекции ваши выходные документы будут содержать только проекционные значения и поле _id, которое может или не может быть тем, что вы хотите). Итак, от вашего запроса агрегации осталось всего $match. Время выполнения запроса можно считать идентичным, за исключением незначительного штрафа за обработку этапа. Кроме того, я лично ожидаю, что «нормальные» find() в значительной степени сойдутся внутри структуры агрегации, если это все равно еще не произошло полностью. Просто не имеет смысла оставлять два способа запроса данных ...

Для ясности, я бы лично склонялся к версии find().

...