Mongodb 在搜索日期范围时在“$lte”(查询或聚合)上存在错误

Mongodb has a bug on "$lte" (query or aggregation) while searching for dates ranges

场景:

我在 MongoDb Atlas 上托管了一个数据库。

此数据库有一个集合,在其他数据中,有一个 Date 类型的字段。

pseudoCode Schema:
... {
created: { type: Date }
}...

我想执行一个查询,使我能够找到集合中存在的所有对象,这些对象在特定日期 including 和边界日期之间具有 created 值。

假设日期范围是 2020-08-012020-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 指南针上

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 形式在逻辑上更适合谁正在阅读代码。

我为什么要发布这个

我确实发现多年来很少有人就此问题发表过帖子,更多相关链接是 this GitHub issue (initially the dev believed the problem was with mongoose, then a solution proposed to check the schema but that's not the issue since in my case the schema is properly defined and I've tested it on Compass directly) and this google group discussion(问题描述不当,没有得到答复)。

但是我没有找到解决方法

虽然我已经快速解决了这个问题,但我想更好地指出它并理解如果:

  • 我做错了什么,这是预期的行为
  • 我的查询有问题
  • $lte 存在问题,需要妥善解决

谁有想法?

快速修复

只需使用 $lt 而不是 $lte,并始终考虑比预期多 1 天。 使用前面的示例,查询将变为

{created: {'$gte': new Date('2020-08-01'), '$lt': new Date('2020-09-01')}}

在这种情况下,我得到了预期日期范围“2020-08-01”-“2020-08-31”的结果。

注意 我也可以使用 $lte 我会得到完全相同的结果,但是 $lt 形式在逻辑上更适合谁正在阅读代码。

当你 运行 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()