MongoDB聚合优化:如何避免$unwind和$group实现马术酒店可用房查询
MongoDB马术酒店集合查询优化方案
你的原有实现逻辑是可行的,但确实存在冗余:两次遍历预订数组做冲突判断+$unwind拆分数组再$group合并的操作会拉高性能开销,数据量较大时查询效率会很低。可以优化为3个阶段完成查询,无需拆分酒店文档,全程在数组内完成计算,逻辑更简洁性能也更好:
优化后聚合管道
const CHECK_IN = ISODate("2021-10-23"); const CHECK_OUT = ISODate("2021-10-24"); const REQUIRED_SPOTS = 2; [ // 阶段1:提前过滤不符合基础条件的酒店,缩小后续计算的数据集 { $match: { status: "approved", active: true } }, // 阶段2:计算所有房间的剩余可订名额,过滤掉无可用名额的房间 { $addFields: { availableRooms: { $filter: { input: { $map: { input: "$rooms", as: "room", in: { // 保留房间原有字段 $mergeObjects: [ "$$room", { // 计算目标日期已预订的名额 reservedSpots: { $reduce: { input: { $ifNull: ["$$room.reservations", []] }, initialValue: 0, in: { $add: [ "$$value", { $cond: [ // 预订时间和目标入住时间冲突则计入已预订 { $and: [ { $lt: ["$$this.fromDate", CHECK_OUT] }, { $gt: ["$$this.toDate", CHECK_IN] } ] }, "$$this.spots", 0 ] } ] } } } } ] } } }, as: "room", // 过滤出剩余名额>0的房间 cond: { $gt: [ { $subtract: ["$$room.spots", "$$room.reservedSpots"] }, 0 ] } } } } }, // 阶段3:统计酒店总剩余名额,过滤出符合要求的酒店 { $match: { $expr: { $gte: [ { $sum: { $map: { input: "$availableRooms", in: { $subtract: ["$$this.spots", "$$this.reservedSpots"] } } } }, REQUIRED_SPOTS ] } } } ]
优化点说明
- 时间冲突判断逻辑更严谨:原逻辑只判断了预订开始日期在入住区间内,优化后的判断覆盖了所有时间冲突场景:只要预订的入住区间和目标区间有重叠就算冲突,避免出现跨日预订漏判的问题。
- 去掉了
$unwind和$group的拆分合并操作,全程在数组内完成计算,文档数不会膨胀,性能提升明显。 - 只用了一次
$reduce遍历预订数组就完成了已预订名额计算,没有重复遍历的冗余操作。 - 保留了所有可用房间的完整信息,无需后续重组字段,返回结果可以直接使用。
内容的提问来源于stack exchange,提问作者Julius
相关产品推荐
相关产品推荐

