MongoDB中$or数组多条件查询时如何用投影获取匹配元素及报错解决
报错原因
- positional操作符
$要求查询条件中必须包含针对单个数组元素的多条件匹配逻辑,你当前使用顶级$or查询时,MongoDB会跨数组元素匹配条件(比如某个元素的startTime满足第一个条件,另一个元素的endTime满足对应条件),无法定位到单个符合全部条件的数组元素,因此$找不到匹配项。
- positional操作符
- 你编写的第三个冲突条件存在笔误:字段名写为
endDate,实际应为endTime,该错误会导致匹配不到符合条件的元素,触发报错。
- 你编写的第三个冲突条件存在笔误:字段名写为
- 代码中直接使用
Date("xxx")返回的是字符串类型,和文档中存储的ISODate日期类型不匹配,也会导致匹配失败触发报错,需要替换为ISODate("xxx")。
- 代码中直接使用
解决方案
时间段冲突的判断逻辑可以先简化:已有时间段[startTime, endTime]和目标时间段[targetStart, targetEnd]冲突的充要条件是 startTime < targetEnd 且 endTime > targetStart,不需要写三层$or,再根据你需要的返回结果选择对应方案即可。
场景1:仅需返回第一个匹配的冲突时间段
将查询逻辑改造为用$elemMatch匹配单个数组元素的冲突条件,再配合$投影即可:
// 目标时间段:2021-09-20T09:55:00.000Z 到 2021-09-20T11:00:00.000Z const targetStart = ISODate("2021-09-20T09:55:00.000Z"); const targetEnd = ISODate("2021-09-20T11:00:00.000Z"); db.collection.find({ timeSlot: { $elemMatch: { startTime: { $lt: targetEnd }, endTime: { $gt: targetStart } } } }, { "timeSlot.$": 1, // 仅返回第一个匹配的冲突时间段 userId: 1, companyId: 1, startDate: 1 })
场景2:需要返回所有匹配的冲突时间段
如果需要拿到数组中所有符合条件的冲突元素,需要使用聚合管道的$filter操作符过滤数组:
const targetStart = ISODate("2021-09-20T09:55:00.000Z"); const targetEnd = ISODate("2021-09-20T11:00:00.000Z"); db.collection.aggregate([ // 第一步:先过滤出存在冲突时间段的文档,减少后续计算量 { $match: { timeSlot: { $elemMatch: { startTime: { $lt: targetEnd }, endTime: { $gt: targetStart } } } } }, // 第二步:过滤timeSlot数组,仅保留符合冲突条件的元素 { $addFields: { timeSlot: { $filter: { input: "$timeSlot", cond: { $and: [ { $lt: ["$$this.startTime", targetEnd] }, { $gt: ["$$this.endTime", targetStart] } ] } } } } } ])
内容的提问来源于stack exchange,提问作者Satizh J
相关产品推荐
相关产品推荐

