必须使用 date 类型字段,否则 $gte/$lte 会按字符串字典序比较;例如 "2024-03-20" 会被判小于 "2024-03-2",因第8位字符 '2'
字段必须是 Date 类型,否则 $gte/$lte 会按字符串字典序比较
直接用
"2024-03-01"这类字符串传给$gte和$lte,MongoDB 就当普通字符串比——"2024-03-20"会被判小于"2024-03-2"(因为第 8 位 `'2'`验证方式很简单:
db.yourCollection.findOne()看字段值是不是ISODate("2024-03-01T00:00:00.000Z")形式。如果是"2024-03-01"(带引号),那就是字符串,得立刻处理。
- 新数据插入时,一律用
new Date("2024-03-01")或dayjs("2024-03-01").toDate(),别拼字符串- 存量字符串数据修复,用聚合管道:
{$set: {createdAt: {$dateFromString: { dateString: "$createdAt" }}}}(仅 MongoDB 4.0+ 支持)- Mongoose Schema 中定义
{ type: Date }不等于自动转换——它只校验输入,不强制转类型;传字符串进来,存进去还是字符串传 Date 实例,不是字符串、时间戳数字或 moment().format()
$gte和$lte接收的是 BSON Date,不是文本也不是毫秒数。传错类型,Mongoose 不会报错,但查询失效或隐式转换(不可靠)。正确写法:
const start = new Date('2024-03-01'); const end = new Date('2024-03-31'); await MyModel.find({ createdAt: { $gte: start, $lte: end } });常见错误写法:
createdAt: { $gte: '2024-03-01', $lte: '2024-03-31' }→ 字符串比较createdAt: { $gte: Date.now(), $lte: Date.now() + 86400000 }→ 传的是 number,不是 Date 对象createdAt: { $gte: moment().format('YYYY-MM-DD') }→ 又是字符串createdAt: { $gte: moment().toDate() }→ ✅ 正确,toDate()返回 Date 实例时区不对齐会导致“查不到当天数据”
new Date('2024-03-01')在东八区生成的是2024-03-01T00:00:00+08:00,Mongoose 发给 MongoDB 时转成 UTC 就成了2024-02-29T16:00:00.000Z——你本想查 3 月 1 日,实际查的是 2 月 29 日下午开始的数据。解决方法取决于业务需求:
- 统一用 UTC 构造:
new Date(Date.UTC(2024, 2, 1))(注意月份 0 起始)- 本地时间对齐当天边界:
start.setHours(0,0,0,0); end.setHours(23,59,59,999)- 用 dayjs 或 date-fns 显式语义:
dayjs('2024-03-01').startOf('day').toDate()、endOfDay(new Date('2024-03-31'))- 避免依赖用户前端传来的字符串日期,后端统一解析并转为本地午夜/UTC 时间再查
没索引的日期范围查询,万级数据就开始卡
$gte/$lte没索引时走 collection scan,性能断崖下跌。哪怕字段在复合索引里,也得满足前缀匹配——比如索引是{ status: 1, createdAt: 1 },那{ createdAt: { $gte: ... } }无法命中,必须把createdAt放索引最左位,或建单独索引。日期类型、构造方式、时区对齐、索引——四个点漏一个,生产环境就可能查不到数据或查慢。尤其要注意:字段存的是字符串,代码里 new Date() 也没用;索引建了但字段顺序不对,等于没建。
- 加单字段索引:
db.collection.createIndex({ createdAt: 1 })- 复合查询场景下,确保日期字段在索引定义中靠前(如
{ createdAt: 1, userId: 1 })- 用
explain("executionStats")验证是否用了索引、扫描文档数是否合理- 时间序列集合(MongoDB 5.0+)自带优化,但需显式创建:
timeField: "createdAt",且查询要带metaField过滤才高效












