<p>必须用 new Date(Date.now() - 3600000) 动态生成时间对象,确保 updatedAt 为 BSON Date 类型并建立合理索引(如 {updatedAt: 1} 或 {status: 1, updatedAt: 1}),避免字符串存储与时区偏差。</p>

查最近一小时数据,必须用 $gte 配合动态生成的 ISODate
硬写死 ISODate("2024-05-20T10:00:00Z") 没用,每次查询都要算出“一小时前”的时间点。MongoDB 不会自动帮你做时间计算,$gte 只认具体时间值。
常见错误是直接在 shell 里写:{ updatedAt: { $gte: ISODate() - 60 * 60 * 1000 } } —— 这会报错,因为 MongoDB Shell(v6.0+)不支持 JS 表达式直接参与查询构造(旧版可能侥幸通过,但不可靠)。
- Node.js 驱动中,用
new Date(Date.now() - 60 * 60 * 1000)生成 JSDate对象,驱动会自动转为 BSONISODate - MongoDB Shell 中,用
new Date(Date.now() - 60 * 60 * 1000)(不是ISODate(...))——ISODate是 shell 封装,但时间运算必须用 JS 原生Date - 聚合管道中如需动态时间,得用
$dateSubtract(v5.0+),例如:{ $dateSubtract: { startDate: "$$NOW", unit: "hour", amount: 1 } }
updatedAt 字段必须是 BSON Date 类型,字符串格式会匹配失败
如果存的是 "2024-05-20T10:30:00Z" 这种字符串,$gte 比较会按字典序执行,结果完全不可信(比如 "9999-01-01" 字符串反而小于 "2024-01-01")。
- 插入时确保用
new Date()或ISODate(),而不是toString()或JSON.stringify() - 用
db.collection.findOne({ updatedAt: { $type: "date" } })检查字段类型;返回空说明可能是字符串 - 批量修复可用
$convert聚合操作(需 v4.0+),但代价高,建议应用层重写时修正
索引没建对,$gte 查询照样慢
即使写了正确的时间条件,若 updatedAt 没建索引,或索引顺序不合理,查询仍要扫全表。
- 最简索引:
db.collection.createIndex({ updatedAt: 1 }) - 若常和状态字段一起查(如
{ updatedAt: { $gte: ... }, status: "active" }),考虑复合索引:db.collection.createIndex({ status: 1, updatedAt: 1 })—— 注意字段顺序:等值查询字段在前,范围查询字段在后 - 用
explain("executionStats")看nReturned和totalDocsExamined是否接近,差太多说明索引没生效
时区陷阱:服务器时间、应用时间、存储时区不一致
ISODate 在 BSON 层面始终是 UTC 时间,但你生成的 new Date() 取决于运行环境时区。本地开发机设为东八区,Date.now() 没问题;但 Node.js 容器若未设 TZ=UTC,new Date() 构造出的时间对象可能含本地偏移,导致入库时间歪掉。
- 统一用
new Date().toUTCString()或直接基于Date.now()计算,避免依赖toLocaleString() - 插入前打日志:
console.log(new Date().toISOString()),确认写入的是预期 UTC 时间 - Shell 中查时,别用
new Date().toLocaleString()去算一小时前——它输出本地时间字符串,传给ISODate()会二次解析出错
实际用的时候,最容易被忽略的是字段类型和索引顺序。字符串时间看着像日期,查出来却为空;复合查询不调换索引字段顺序,性能就卡在毫秒级变秒级。


















