Почему MongoDB не фильтрует по дате ($gte $lt)?

MongoDB может некорректно фильтровать даты при использовании операторов $gte и $lt, если даты представлены как строковые значения в формате ISO 8601. Это происходит из-за неправильной лексикографической сортировки строковых значений.

В MongoDB, по умолчанию, даты хранятся в специальном формате BSON (Binary JSON), который представляет дату как 64-битное целое число, представляющее количество миллисекунд, прошедших с полуночи 1 января 1970 года (также известного как "Unix timestamp"). Этот формат позволяет выполнять правильные операции сравнения дат.

Однако, если даты передаются или хранятся в виде строковых значений, MongoDB их не преобразует в формат BSON автоматически. Вместо этого, MongoDB будет использовать лексикографическую сортировку для сравнения строк.

Например, допустим у вас есть документы со следующими значениями даты (в строковом формате ISO 8601):

{ "date": "2019-01-01T00:00:00Z" }
{ "date": "2019-01-02T00:00:00Z" }
{ "date": "2019-01-03T00:00:00Z" }

Если вы выполните запрос на фильтрацию по дате с использованием операторов $gte и $lt, то результат будет неправильным:

db.collection.find({ date: { $gte: "2019-01-02T00:00:00Z", $lt: "2019-01-03T00:00:00Z" } })

Результат будет содержать только один документ с датой "2019-01-02T00:00:00Z", вместо ожидаемых двух документов с датами "2019-01-02T00:00:00Z" и "2019-01-03T00:00:00Z". Это происходит потому, что лексикографически "2019-01-03T00:00:00Z" считается больше, чем "2019-01-02T00:00:00Z" и не соответствует условию $lt.

Чтобы избежать этой проблемы, необходимо преобразовать строки даты в формат BSON. В MongoDB для этого есть оператор $dateFromString, который позволяет выполнять преобразование строки в дату. Например:

db.collection.aggregate([
  {
    $addFields: {
      date: { $dateFromString: { dateString: "$date" } }
    }
  },
  {
    $match: {
      date: { $gte: ISODate("2019-01-02T00:00:00Z"), $lt: ISODate("2019-01-03T00:00:00Z") }
    }
  }
])

Оператор $addFields добавляет новое поле date, которое преобразовывает строковое значение date в формат BSON. Затем, оператор $match применяет фильтр по дате, используя операторы $gte и $lt, и на этот раз результат будет правильным.

Важно отметить, что хранение дат в формате BSON является более эффективным с точки зрения использования памяти и производительности запросов, поэтому рекомендуется использовать формат BSON для хранения дат в MongoDB. Если вам необходимо иметь даты в строковом формате для определенных целей, рекомендуется сохранять даты в формате BSON и преобразовывать их в строковый формат, когда это необходимо, с использованием функции преобразования данных в вашем приложении.