MongoDB按条件查询数组内对象 查找指定时段餐厅可用桌台
问题排查与修复方案
原有代码的核心问题
- 错误1:查询条件
"tables[0].bookings"硬编码了数组下标0,仅校验第1张桌台的预订记录,不会遍历所有桌台 - 错误2:
findOne默认返回完整的门店文档,不会单独返回匹配条件的桌台对象 - 错误3:后续操作直接修改门店文档的
isBooked和bookings字段,完全没有命中对应桌台的属性,写入逻辑完全错误
最优解决方案:原子更新(避免并发超订)
推荐直接用findOneAndUpdate单查询完成「校验可用桌台+写入预订信息」的操作,全程是原子操作,不会出现多个用户同时抢到同一张桌的问题:
const mongoose = require('mongoose'); const stringDate = new Date(date).toLocaleDateString(); const placeId = req.body.placeId; const bookingData = { Name: name, Time: time, Date: stringDate, numOfPersons: parseInt(personsNumber), Note: note, User: userId, }; // 1. 匹配条件:指定门店,且存在至少一张桌台没有当前时段的预订记录 // 2. 更新操作:给第一张符合条件的桌台推入新的预订信息 // 3. 配置项返回更新后的文档、只返回匹配的桌台 const result = await Places.findOneAndUpdate( { _id: placeId, tables: { $elemMatch: { bookings: { $not: { $elemMatch: { Date: stringDate, Time: time } } } } } }, { $push: { "tables.$.bookings": bookingData } // 如果isBooked是时段标记、而非桌台全局占用标记,可以加下面这行同步更新 // $set: { "tables.$.isBooked": true } }, { new: true, // 返回更新后的文档 projection: { tables: { $elemMatch: { bookings: { $not: { $elemMatch: { Date: stringDate, Time: time } } } } } } ); // 拿到最终被预订的桌台信息 const bookedTable = result.tables[0];
注意:你现有Schema里的
isBooked字段设计不符合分时段预订的场景,同一张桌不同时段可以被不同用户预订,不要全局修改该字段为true,否则其他时段的用户无法正常预订该桌。
如果你需要先返回所有可用桌台给用户选择,再确认预订,可以用聚合查询先查可用桌台列表:
const availableTables = await Places.aggregate([ // 先匹配指定门店 { $match: { _id: mongoose.Types.ObjectId(placeId) } }, // 拆分tables数组为单独的文档 { $unwind: "$tables" }, // 过滤掉当前时段已经被预订的桌台 { $match: { "tables.bookings": { $not: { $elemMatch: { Date: stringDate, Time: time } } } } }, // 只返回桌台信息,剔除门店其他冗余字段 { $replaceRoot: { newRoot: "$tables" } } ]);
内容的提问来源于stack exchange,提问作者Rawand122
相关产品推荐
相关产品推荐

