MongoDB聚合查询验证营业时间失效问题排查求助
问题:MongoDB营业时间验证查询失效
数据模型
用户使用的Mongoose Schema如下:
const registerSchema = new Schema( { UserName: { type: String, requires: true, }, Contact: { type: String, requires: false, }, Email: { type: String, requires: false, }, Password: { type: String, requires: true, }, RoleCode: { type: String, enum: ["001", "111", "222"], // 001-user, 111-manager, 222-admin requires: true, }, openingHours: [ { day: { type: Number, }, startTime: { type: Number, }, endTime: { type: Number, }, }, ], }, { timestamps: true });
当前使用的聚合查询
用户编写的用于验证预订时间是否在营业时间内的聚合代码:
const inOpeningHours = await Register.aggregate([ { $match: { _id: managerId, }, }, { $unwind: "$openingHours", // Unwind the openingHours array }, { $match: { $or: [ { $and: [ { "openingHours.day": { $eq: reservationData.startTime.getDay(), }, }, { "openingHours.startTime": { $gt: reservationData.startTime.getHours(), }, }, ], }, { $and: [ { "openingHours.day": { $eq: reservationData.endTime.getDay(), }, }, { "openingHours.endTime": { $lt: reservationData.endTime.getHours(), }, }, ], }, ], }, }, ]);
问题原因
- 条件运算符完全写反:
验证逻辑应该是「预订开始时间不早于营业开始时间,且预订结束时间不晚于营业结束时间」,但原查询用$gt(营业开始时间 > 预订开始小时)、$lt(营业结束时间 < 预订结束小时),这会导致只有当预订时间完全在营业时间之外时才会匹配,完全违背需求。 - 逻辑运算符错误:
原查询使用$or,意味着只要满足其中一个条件就会返回结果,但我们需要的是整个预订时段都在营业时间内,应该使用$and来确保所有条件同时满足。 - 不必要的
$unwind:
使用$unwind会展开数组,增加查询复杂度,直接用$elemMatch可以更精准高效地匹配数组中符合条件的元素。
修正方案
情况1:预订时间在同一天
如果用户的预订不会跨天,可以直接用find配合$elemMatch完成验证,无需聚合:
const startDay = reservationData.startTime.getDay(); const startHour = reservationData.startTime.getHours(); const endHour = reservationData.endTime.getHours(); const valid = await Register.exists({ _id: managerId, openingHours: { $elemMatch: { day: startDay, startTime: { $lte: startHour }, endTime: { $gte: endHour } } } }); // 如果valid为true,说明预订时间在营业时间内
情况2:预订时间跨天
如果允许预订跨天(比如从周一晚到周二凌晨),需要分别验证两天的时段是否符合:
const startDay = reservationData.startTime.getDay(); const endDay = reservationData.endTime.getDay(); const startHour = reservationData.startTime.getHours(); const endHour = reservationData.endTime.getHours(); let valid = false; if (startDay === endDay) { // 同一天验证逻辑 valid = await Register.exists({ _id: managerId, openingHours: { $elemMatch: { day: startDay, startTime: { $lte: startHour }, endTime: { $gte: endHour } } } }); } else { // 跨天验证:开始日的营业结束时间 >= 预订开始小时,且结束日的营业开始时间 <= 预订结束小时 valid = await Register.exists({ _id: managerId, $and: [ { openingHours: { $elemMatch: { day: startDay, endTime: { $gte: startHour } } } }, { openingHours: { $elemMatch: { day: endDay, startTime: { $lte: endHour } } } } ] }); }
说明
使用exists方法可以直接判断是否存在符合条件的文档,比聚合更简洁高效。如果需要获取具体的营业时间数据,可以把exists换成findOne。
内容的提问来源于stack exchange,提问作者Ranjit Sapkota
相关产品推荐
相关产品推荐

