#database #mongodb #date #mongodb-compass #non-relational-database
#База данных #mongodb #Дата #mongodb-compass #нереляционная база данных
Вопрос:
Сценарий:
У меня есть база данных, размещенная в MongoDB Atlas.
В этой базе данных есть коллекция, которая, среди прочих данных, имеет created
поле типа Date
.
pseudoCode Schema:
... {
created: { type: Date }
}...
Я хочу выполнить запрос, который позволит мне найти все объекты, существующие в коллекции, которые имеют created
значение между конкретными днями including
и граничными датами.
Давайте предположим, что диапазон дат 2020-08-01
и 2020-08-31
, запрос будет
{created: {'$gte': new Date('2020-08-01'), '$lte': new Date('2020-08-31')}}
верно?
Неправильно.
Выполняя запрос таким образом, я получаю только результаты, которые являются greater than or equal to
«2020-08-01» и lower than
«2020-08-31». Это означает, что, даже если я выполняю $lte
запрос, я всегда получаю $lt
результаты.
Тесты, которые я сделал
Я тестировал это только для поля даты типа atm в разных коллекциях и постоянно сталкивался с одной и той же проблемой. Пока не было времени для дальнейших исследований по различным типам данных.
Я тестировал это на aggregation $match
конвейерах и find
запросах на:
- моя кодовая база
- чистый скрипт, который просто выполняет эти операции
- непосредственно в MongoDB Compass
Во всех 3 случаях результаты соответствуют выявленной проблеме и подтверждают проблему.
Быстрое исправление
Просто используйте $lt
вместо $lte
и всегда учитывайте на 1 день больше, чем вы предполагали. Используя предыдущий пример, запрос станет
{created: {'$gte': new Date('2020-08-01'), '$lt': new Date('2020-09-01')}}
и в этом случае я получаю результаты ожидаемого диапазона дат «2020-08-01» — «2020-08-31».
Обратите внимание, что я мог бы также использовать $lte
, и я бы получил точно такие же результаты, однако $lt
форма логически более правильна для тех, кто читает код.
Почему я публикую это
Я обнаружил, что за эти годы мало кто сообщал об этой проблеме, более релевантными ссылками являются эта проблема с GitHub (первоначально разработчик полагал, что проблема связана с mongoose, затем было предложено решение для проверки схемы, но это не проблема, поскольку в моем случае схема определена правильно, и я протестировал ее на Compass напрямую) и это групповое обсуждение Google (проблема плохо сформулирована и не получила ответа).).
Но я не нашел решения.
Несмотря на то, что я быстро исправил проблему, я хотел бы лучше указать на это и понять, если:
- Я делаю что-то не так, и это ожидаемое поведение
- в моем запросе я что-то делаю неправильно
- существует проблема, с
$lte
которой необходимо правильно обращаться
У кого есть идеи?
Ответ №1:
Когда вы запускаете new Date('2020-08-01')
, результат на самом деле ISODate("2020-08-01T00:00:00Z")
Итак
{created: {'$gte': new Date('2020-08-01'), '$lte': new Date('2020-08-31')}}
становится
{created: {'$gte': ISODate("2020-08-01T00:00:00Z"), '$lte': ISODate("2020-08-31T00:00:00Z")}}
т. е. день 2020-08-31 не включен. Вы также можете учитывать часовые пояса, если данные были вставлены как местное время и, следовательно, сохранены не как 2020-08-02T00:00:00Z
но 2020-08-02T02:00:00Z
, например.
Одним из решений является добавление одного дня и использование $lt
:
{created: {'$gte': new Date('2020-08-01'), '$lt': new Date('2020-09-01')}}
или вы можете использовать Moment.js вот так:
{created: {'$gte': new Date('2020-08-01'), '$lte': moment.utc('2020-08-31').endOf('day').toDate()}}
или, возможно moment.utc('2020-08-01').endOf('month').toDate()
Комментарии:
1. это то, о чем я не подумал! Я был сосредоточен на дате и полностью проигнорировал рассмотрение часовой части, которая все еще передается после преобразования в запросе. О! Я жду еще немного, чтобы узнать, есть ли какие-либо другие ответы, но я думаю, что у нас есть победитель здесь. Спасибо!
Ответ №2:
Быстрое исправление
Просто используйте $lt
вместо $lte
и всегда учитывайте на 1 день больше, чем вы предполагали. Используя предыдущий пример, запрос станет
{created: {'$gte': new Date('2020-08-01'), '$lt': new Date('2020-09-01')}}
и в этом случае я получаю результаты ожидаемого диапазона дат «2020-08-01» — «2020-08-31».
Обратите внимание, что я мог бы также использовать $lte
, и я бы получил точно такие же результаты, однако $lt
форма логически более правильна для тех, кто читает код.