Mongoose如何实现日期时间区间的多条件组合查询
Mongoose 操作 MongoDB 多条件+时间范围查询实现
现有集合结构
项目中报告集合的示例数据如下:
[ { _id: new ObjectId("62ae97b6be08b688f93f2c07"), reportId: '1', method: 'A1', category: 'B2', date: '2022-06-19', time: '22:55', emergency: 'normal', __v: 0 }, { _id: new ObjectId("62ae97b6be08b688f93f2c08"), reportId: '2', method: 'A3', category: 'B5', date: '2022-06-18', time: '23:05', emergency: 'normal', __v: 0 }, { _id: new ObjectId("62ae97b6be08b688f93f2c09"), reportId: '3', method: 'A5', category: 'B1', date: '2022-06-19', time: '23:55', emergency: 'urgent', __v: 0 } ]
已实现基础逻辑
当前已经完成method、emergency、category三个字段的枚举值匹配筛选,代码如下:
const options = [ { method: { $in: ['A1','A2'] } }, { emergency: { $in: data.emergency } }, { category: { $in: data.category } } ]; const response = await Report.find({ $or: options,});
待实现需求
新增时间范围筛选规则:集合内date、time字段均为字符串类型,需要筛选出时间落在前一日23点之后、当日23点之前的所有数据。
之前尝试使用$where结合moment.js编写测试逻辑,不确定正确性,测试代码如下:
date: { $where: function () { const yesterday = moment().subtract(1, 'days').format('YYYYMMDD') + '2300'; const date = moment(this.date).format('YYYYMMDD') + this.time.replace(':', ''); const today = moment().format('YYYYMMDD') + '2300'; return yesterday < date && date <= today; }, },
问题说明与正确实现
原有$where写法的问题
- 语法错误:
$where是顶级查询操作符,不能嵌套在date字段的查询对象内 - 执行错误:
$where的逻辑在MongoDB服务端运行,默认环境没有moment依赖,直接调用会抛出异常 - 性能缺陷:
$where会触发全表扫描,无法命中索引,数据量较大时查询延迟极高,生产环境不推荐使用
推荐方案(原生操作符实现,性能最优)
由于存储的date字段为YYYY-MM-DD格式、time字段为HH:mm格式,字符串的字典排序规则和实际时间顺序完全一致,不需要做日期类型转换,直接通过字符串比较即可实现范围筛选,还可以支持联合索引优化查询速度。
时间范围可以拆分为两个并列区间匹配:
- 日期为前一天,且时间大于
23:00 - 日期为当天,且时间小于
23:00
完整查询代码如下,注意两个$or条件需要用$and包裹,避免同名字段覆盖:
const moment = require('moment'); // 提前在Node侧计算边界日期,自动处理跨月、跨年场景 const yesterday = moment().subtract(1, 'days').format('YYYY-MM-DD'); const today = moment().format('YYYY-MM-DD'); const baseFilterOptions = [ { method: { $in: ['A1','A2'] } }, { emergency: { $in: data.emergency } }, { category: { $in: data.category } } ]; const response = await Report.find({ $and: [ // 原有基础筛选条件 { $or: baseFilterOptions }, // 新增时间范围筛选 { $or: [ { date: yesterday, time: { $gt: '23:00' } }, { date: today, time: { $lt: '23:00' } } ] } ] });
不推荐的$where修正写法(仅作参考)
如果特殊场景必须使用$where,需要提前在Node侧计算好时间阈值,以字符串拼接的方式传入查询语句,不要在服务端执行逻辑里依赖外部库:
const moment = require('moment'); const yesterdayThreshold = moment().subtract(1, 'days').format('YYYYMMDD') + '2300'; const todayThreshold = moment().format('YYYYMMDD') + '2300'; const response = await Report.find({ $and: [ { $or: baseFilterOptions }, { $where: `function() { const recordTime = this.date.replace(/-/g, '') + this.time.replace(':', ''); return recordTime > '${yesterdayThreshold}' && recordTime < '${todayThreshold}'; }` } ] });
内容的提问来源于stack exchange,提问作者hao
相关产品推荐
相关产品推荐

