MongoDB聚合管道填充缺失日期(无法使用$densify)
解决MongoDB聚合显示日期范围内所有日期(含空任务日期)的方案
由于无法使用$densify,可以通过以下两种方式实现需求:
方案一:MongoDB 4.4+ 版本使用$documents + $lookup
利用$documents注入目标日期范围的所有日期,再通过左连接关联原任务数据,确保无任务日期保留空数组。
代码实现
const startDate = new Date("2022-10-19"); const endDate = new Date("2022-10-26"); // 生成日期范围内的所有日期(统一时间部分为00:00:00,匹配数据库date字段格式) const allDates = []; let currentDate = new Date(startDate); while (currentDate <= endDate) { const dateOnly = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()); allDates.push({ date: dateOnly }); currentDate.setDate(currentDate.getDate() + 1); } const pipeline = [ // 注入所有日期作为初始文档 { $documents: allDates }, // 左连接原集合中对应日期的任务 { $lookup: { from: "scheduleTasks", // 替换为你的集合实际名称 let: { targetDate: "$date" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$date", "$$targetDate"] }, { $gte: ["$date", startDate] }, { $lte: ["$date", endDate] } ] } } } ], as: "tasks" } }, // 格式化结果结构,与原管道输出保持一致 { $project: { _id: "$date", tasks: 1 } }, // 按日期倒序排序 { $sort: { _id: -1 } } ]; const aggregationData = await ScheduleTaskModel.aggregate(pipeline);
说明
$documents会将生成的日期数组作为聚合的初始数据源$lookup使用左连接,确保每个日期都保留,无匹配任务时tasks自动为空数组- 需保证生成的日期与数据库中
date字段的时间格式一致,避免匹配失败
方案二:低版本MongoDB 代码层面合并结果
如果你的MongoDB版本低于4.4,无法使用$documents,可以在服务端代码中生成日期范围,再与原聚合结果合并。
代码实现
// 生成日期范围的工具函数 function generateDateRange(startDateStr, endDateStr) { const dates = []; const start = new Date(startDateStr); const end = new Date(endDateStr); let current = new Date(start); while (current <= end) { // 统一时间部分为00:00:00 const dateOnly = new Date(current.getFullYear(), current.getMonth(), current.getDate()); dates.push(dateOnly); current.setDate(current.getDate() + 1); } return dates; } // 原聚合管道:获取有任务的日期数据 const originalPipeline = [ { $match: { date: { $gte: new Date("2022-10-19"), $lte: new Date("2022-10-26") }, }, }, { $group: { _id: "$date", tasks: { $push: "$$ROOT" }, }, } ]; // 获取原聚合结果 const originalData = await ScheduleTaskModel.aggregate(originalPipeline); // 生成目标日期范围内的所有日期 const allDates = generateDateRange("2022-10-19", "2022-10-26"); // 将原数据转为Map,快速查找对应日期的任务 const dateDataMap = new Map(); originalData.forEach(item => { const dateKey = item._id.toISOString(); dateDataMap.set(dateKey, item); }); // 合并结果,确保所有日期都存在 const aggregationData = allDates.map(date => { const dateKey = date.toISOString(); const existingItem = dateDataMap.get(dateKey); return existingItem ? existingItem : { _id: date, tasks: [] }; }).sort((a, b) => b._id - a._id); // 按日期倒序排序
说明
- 先通过原聚合获取有任务的日期数据,再在代码中补全缺失的日期
- 使用
Map存储原数据,提升查找效率 - 最后排序保持与原管道一致的倒序逻辑
内容的提问来源于stack exchange,提问作者Vaggelis
相关产品推荐
相关产品推荐

