Mongoose复杂查询构建:筛选指定时段可用的Pro
如何用Mongoose筛选符合时段要求且非不可用的Pro?
我来帮你完善这个查询,咱们一步步拆解剩下的需求:匹配指定时段的可用时间、排除不可用时段,还要过滤掉状态不可用的Pro。
第一步:先把请求的时间转换成便于对比的格式
首先,我们需要把请求的date_start转换成当天的总分钟数,同时计算出时段结束的总分钟数,这样就能和availability里的h、m直接对比了:
const date_start = new Date(req.body.date_start); const duration = req.body.duration; // 单位是小时 const date_finish = new Date(date_start.getTime() + (1000 * 60 * 60 * duration)); // 计算当天的起始分钟数(比如12:30就是12*60+30=750) const startHour = date_start.getHours(); const startMinute = date_start.getMinutes(); const startTotalMin = startHour * 60 + startMinute; // 计算时段结束的总分钟数 const endTotalMin = startTotalMin + duration * 60; const targetDay = parseInt(date_start.getDay(), 10);
第二步:完善查询条件
我们需要在现有查询基础上,添加三个关键条件:
- 匹配availability的可用时段:找到对应星期几的availability条目,确保请求的时段完全落在该条目的
from到to之间 - 排除unavailability的冲突时段:确保Pro的不可用时段和请求的时段没有重叠
- 排除不可用状态的Pro:过滤掉
status为disabled的Pro
完整的查询代码如下:
const availablePros = await Pro.find({ // 已有的筛选条件 services: req.body.service_id, zone: req.body.zone_id, // 排除不可用状态 status: { $ne: "disabled" }, // 匹配对应星期几的可用时段,且请求时段完全在该时段内 availability: { $elemMatch: { day: targetDay, $expr: { $and: [ // 可用时段的起始分钟 <= 请求时段起始分钟 { $lte: [{ $add: ["$from.h", { $multiply: ["$from.m", 1/60] }] }, startHour + startMinute/60] }, // 可用时段的结束分钟 >= 请求时段结束分钟 { $gte: [{ $add: ["$to.h", { $multiply: ["$to.m", 1/60] }] }, (startHour + startMinute/60) + duration] } ] } } }, // 排除存在冲突不可用时段的Pro:没有任何一个unavailability条目和请求时段重叠 unavailability: { $not: { $elemMatch: { from: { $lt: date_finish }, to: { $gt: date_start } } } } }).populate('services zone'); // 按需关联查询关联集合
关键条件解释
- availability的$expr用法:因为我们需要把文档里的
from.h、from.m转换成小时数(或者总分钟数)和请求的时间对比,用$expr可以在查询里执行算术运算,避免在代码里做额外转换。 - unavailability的冲突判断:两个时间段重叠的逻辑是「不可用时段的开始 < 请求时段的结束,且不可用时段的结束 > 请求时段的开始」,用
$not和$elemMatch就能排除所有存在这种冲突的Pro。 - status过滤:直接排除
status为disabled的Pro,如果你只有confirmed状态是可用的,也可以改成status: "confirmed"。
结合你的示例数据验证
比如你提供的Pro 3(_id: 5ae5946a8c09fc67c714597d):
- 它的
services和zone都匹配请求参数,但status是disabled,会被第一个过滤条件排除; - 同时它的
unavailability时段和请求的date_start(假设是周日,对应day=0)也可能冲突,双重保障不会被筛选出来。
内容的提问来源于stack exchange,提问作者Gianluca Cesari
相关产品推荐
相关产品推荐

