MongoDB中如何通过日期数组筛选指定数据?
MongoDB聚合查询中日期数组筛选返回空结果的修正方案
问题场景
MongoDB文档包含scheduleStartDate字段,先通过范围筛选数据:
$match: { jobId: { $in: value.jobIds }, scheduleStartDate: { $gte: startDate, $lte: endDate }, }
后续需要通过API传入的日期数组(示例:["11-03-2021","12-03-2021",...])进一步过滤,但添加的逻辑返回空结果:
if (value.dates) { value.dates = value.dates.map((date) => { return moment(date, "DD/MM/YYYY").format("YYYY-MM-DD[T]HH:mm:ss"); }); filterArray.push({ $match: { $and: {scheduleStartDate: value.dates}, }, }); }
完整聚合代码如下:
const result = await slotModel.aggregate([ { $match: { userId: { $in: value.userIds }, scheduleStartDate: { $gte: startDate, $lte: endDate }, }, }, { $lookup: { from: "myjobs", localField: "jobId", foreignField: "_id", as: "job", }, }, { $match: { $and: filterArray, }, }, { $project: { round: 1, type: 1, mode: 1, scheduleStartDate: 1, scheduleEndDate: 1, feedbackId: 1, }, }, ]); return result;
问题根源
- 日期解析格式错误:API传入的日期用
-分隔(如11-03-2021),但代码中用DD/MM/YYYY解析,导致生成的日期完全错误。 $match条件结构错误:将$match阶段对象推入filterArray,再用$and包裹,导致聚合条件格式不符合MongoDB要求;同时错误地直接将数组赋值给scheduleStartDate,未使用$in操作符。- 日期精度不匹配:原文档
scheduleStartDate含时分秒,转换后的日期默认是当天零点,若原数据时间不是零点则无法匹配。
修正方案
方案1:精确匹配日期(含时分秒)
如果需要严格匹配scheduleStartDate等于指定日期的零点时刻,修正代码如下:
// 构建filterArray的逻辑 if (value.dates) { // 注意传入日期是DD-MM-YYYY格式,用对应格式解析 const targetDates = value.dates.map(date => moment(date, "DD-MM-YYYY").startOf('day').toDate() ); // 直接推入匹配条件,而非$match阶段 filterArray.push({ scheduleStartDate: { $in: targetDates } }); } // 聚合查询保持原结构,注意filterArray里是条件对象,不是$match阶段 const result = await slotModel.aggregate([ { $match: { userId: { $in: value.userIds }, scheduleStartDate: { $gte: startDate, $lte: endDate }, }, }, { $lookup: { from: "myjobs", localField: "jobId", foreignField: "_id", as: "job", }, }, // 直接用$match包裹filterArray,无需额外$and(MongoDB默认逻辑与) { $match: { $and: filterArray } }, { $project: { round: 1, type: 1, mode: 1, scheduleStartDate: 1, scheduleEndDate: 1, feedbackId: 1, }, }, ]);
方案2:匹配日期部分(忽略时分秒)
如果只需匹配日期,不管具体时分秒,推荐用$dateTrunc截断日期到天再匹配:
const pipeline = [ { $match: { userId: { $in: value.userIds }, scheduleStartDate: { $gte: startDate, $lte: endDate }, }, }, { $lookup: { from: "myjobs", localField: "jobId", foreignField: "_id", as: "job", }, } ]; if (value.dates) { // 转换为YYYY-MM-DD格式的字符串数组 const targetDateStrs = value.dates.map(date => moment(date, "DD-MM-YYYY").format("YYYY-MM-DD") ); // 添加字段存储截断后的日期 pipeline.push({ $addFields: { scheduleDate: { $dateTrunc: { date: "$scheduleStartDate", unit: "day" } } } }); // 匹配日期字符串 pipeline.push({ $match: { $expr: { $in: [ { $dateToString: { format: "%Y-%m-%d", date: "$scheduleDate" } }, targetDateStrs ] } } }); } // 添加投影阶段,移除临时字段 pipeline.push({ $project: { round: 1, type: 1, mode: 1, scheduleStartDate: 1, scheduleEndDate: 1, feedbackId: 1, scheduleDate: 0 } }); const result = await slotModel.aggregate(pipeline);
关键注意事项
- 务必确保日期解析格式与传入格式一致:API用
DD-MM-YYYY,则moment解析时必须用对应格式,否则会生成错误日期。 - 不要将
$match阶段对象推入filterArray,应直接推入条件键值对,避免聚合结构错误。
内容的提问来源于stack exchange,提问作者Tushar
相关产品推荐
相关产品推荐

