Node.js中MongoDB同集合仅返回指定日期的availability元素
MongoDB查询:按需返回指定日期区间的availability数据
问题场景
我有如下MongoDB集合示例:
{"merchantId": {"$oid": "62c950dfc96c2b690028be88"},"contactInfo": {"name": "Claudia Shields","phoneNumber": 8904672101},"location": {"address": "737 applegate court","city": "bowie","state": "rhode island","country": "greece","zipCode": 10825},"price": 16,"parkingType": "residence","parkingInfo": [{"parkingName": "Fountain Avenue","_id": {"$oid": "62d12053cb03235286511d55"},"default": []}],"totalSpots": [127],"coordinates": {"lng": 34.048954,"lat": 10.299556},"status": "active","isFeePaid": false,"parkingZone": [],"availability": [{"day": "Saturday","startTime": "09:00","endTime": "14:00","_id": {"$oid": "62d13355bd1ad85d85ae4b4b"}},{"day": "Monday","startTime": "08:00","endTime": "16:00","_id": {"$oid": "62d13375bd1ad85d85ae4b4f"}},{"day": "Tuesday","startTime": "08:00","endTime": "16:00","_id": {"$oid": "62d1337cbd1ad85d85ae4b53"}},{"day": "Wednesday","startTime": "08:00","endTime": "16:00","_id": {"$oid": "62d13383bd1ad85d85ae4b57"}},{"day": "Thursday","startTime": "08:00","endTime": "16:00","_id": {"$oid": "62d13390bd1ad85d85ae4b5b"}},{"day": "Friday","startTime": "08:00","endTime": "16:00","_id": {"$oid": "62d13396bd1ad85d85ae4b5f"}}]}
通过Postman发送的请求参数:
{"city":"edmund","startTime": "2022-07-25 11:00","duration":{"days":"0","hours":"3","minutes":"00"}}
需求明确:
- 2022年7月25日是周一,请求时长3小时(未超过24小时),仅返回周一的availability数据,过滤掉周二至周六的条目;
- 若请求时长超过24小时,则仅返回周一和周二的availability数据。
我尝试用$elemMatch做投影,但代码仍返回所有availability条目:
exports.getParkingListByCriteria = async (req, res) => { try { endTime = getEndTime(req.body.startTime, req.body.duration); const startTime = dayjs(req.body.startTime, "YYYY-MM-DD HH:mm").format("dddd"); let parkings = await Parking.find( { "location.city": req.body.city}, { "availability.day": { $elemMatch: { day: startTime } } } // 试过这个写法,但仍返回周一到周六的所有availability,硬写"Monday"也没用 ); parkingList = []; parkings.filter((parking) => { if (isParkingAvailable(parking.availability, req.body.startTime, endTime)) parkingList.push(parking); }); res.status(200).send(parkingList); } catch (error) { return res.status(500).json({ error: error.message }); } };
解决方案
问题根源
你用$elemMatch的写法错误,"availability.day": { $elemMatch: ... }只会匹配数组元素里的day字段,不会过滤整个数组条目。正确做法是直接对availability数组使用过滤逻辑,同时要根据请求时长判断需要保留哪些星期几的数据。
方法一:聚合管道过滤(推荐,数据库层面处理,性能更优)
exports.getParkingListByCriteria = async (req, res) => { try { const { city, startTime: reqStartTime, duration } = req.body; const start = dayjs(reqStartTime, "YYYY-MM-DD HH:mm"); const end = getEndTime(reqStartTime, duration); const endDayjs = dayjs(end); // 确定需要保留的星期几 const requiredDays = []; const startDay = start.format("dddd"); requiredDays.push(startDay); // 时长超过24小时则添加下一天 if (endDayjs.diff(start, 'hour') >= 24) { const nextDay = start.add(1, 'day').format("dddd"); requiredDays.push(nextDay); } // 用聚合管道完成文档匹配+数组过滤 let parkings = await Parking.aggregate([ // 第一步:匹配目标城市 { $match: { "location.city": city } }, // 第二步:过滤availability数组,只保留指定星期几的条目 { $addFields: { availability: { $filter: { input: "$availability", as: "avail", cond: { $in: ["$$avail.day", requiredDays] } } } } }, // 可选:过滤掉availability为空的无效文档 { $match: { "availability.0": { $exists: true } } } ]); // 继续执行你的可用性判断逻辑 const parkingList = parkings.filter(parking => isParkingAvailable(parking.availability, reqStartTime, end) ); res.status(200).send(parkingList); } catch (error) { return res.status(500).json({ error: error.message }); } };
方法二:查询后在代码层过滤数组(小数据量场景适用)
如果不想用聚合,也可以在拿到数据库返回的文档后,手动过滤availability数组:
exports.getParkingListByCriteria = async (req, res) => { try { endTime = getEndTime(req.body.startTime, req.body.duration); const start = dayjs(req.body.startTime, "YYYY-MM-DD HH:mm"); const endDayjs = dayjs(endTime); // 确定需要保留的星期几 const requiredDays = [start.format("dddd")]; if (endDayjs.diff(start, 'hour') >= 24) { requiredDays.push(start.add(1, 'day').format("dddd")); } // 先查询匹配城市的所有文档 let parkings = await Parking.find({ "location.city": req.body.city }); // 过滤文档+处理availability数组 const parkingList = parkings.filter(parking => { // 只保留需要的星期几的availability条目 parking.availability = parking.availability.filter(avail => requiredDays.includes(avail.day) ); // 过滤掉无有效availability的文档,再执行可用性判断 return parking.availability.length > 0 && isParkingAvailable(parking.availability, req.body.startTime, endTime); }); res.status(200).send(parkingList); } catch (error) { return res.status(500).json({ error: error.message }); } };
核心说明
$filter聚合操作符可以精准筛选数组中符合条件的元素,是数据库层面过滤数组的最优方式;- 用
dayjs的diff方法计算时间间隔,判断是否需要包含下一天的availability数据; - 大数据量场景优先用聚合管道,减少数据库传输到服务端的数据量;小数据量场景用代码层过滤更简洁。
内容的提问来源于stack exchange,提问作者Talc
相关产品推荐
相关产品推荐

