如何为MongoDB聚合设置默认值?补全无维修日期计数零值
解决MongoDB聚合查询补全无维修记录日期count为0的问题
需求:获取指定时间段内的每日维修统计数据,无维修记录的日期需将count字段默认设为0。当前聚合查询仅返回存在维修记录的日期,示例输出如下:
[ { day: 21, month: 10, year: 2022, count: 2 }, { day: 28, month: 10, year: 2022, count: 1 }, { day: 24, month: 10, year: 2022, count: 2 } ]
现有聚合查询代码:
const result = await Repair.aggregate([ { $match: { createdDate: { $gte: new Date(fromDate), $lte: new Date(toDate), }, }, }, { $group: { _id: { day: "$day", year: "$year", month: "$month", }, count: { $sum: 1, }, }, }, { $project: { _id: 0, day: "$_id.day", month: "$_id.month", year: "$_id.year", count: "$count", }, }, ]);
修改方案
MongoDB聚合本身无法直接生成日期序列,需先构造出查询区间内的所有日期,再与原统计结果做左外连接,补全无数据日期的count为0。
完整修改后的代码
// 计算日期区间的天数差,生成所有日期的基础数据 const start = new Date(fromDate); const end = new Date(toDate); const daysDiff = Math.ceil((end - start) / (1000 * 60 * 60 * 24)) + 1; const result = await Repair.aggregate([ // 1. 生成目标区间内的所有日期文档 { $documents: Array.from({ length: daysDiff }, (_, i) => { const date = new Date(start); date.setDate(start.getDate() + i); return { year: date.getFullYear(), month: date.getMonth() + 1, // 月份从1开始匹配原数据格式 day: date.getDate() }; }) }, // 2. 左连接原维修统计结果 { $lookup: { from: "repairs", // 替换为你的Repair集合实际名称 let: { targetYear: "$year", targetMonth: "$month", targetDay: "$day" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$year", "$$targetYear"] }, { $eq: ["$month", "$$targetMonth"] }, { $eq: ["$day", "$$targetDay"] }, { $gte: ["$createdDate", new Date(fromDate)] }, { $lte: ["$createdDate", new Date(toDate)] } ] } } }, { $count: "count" } ], as: "repairStats" } }, // 3. 处理连接结果,补全零值 { $project: { year: 1, month: 1, day: 1, count: { $ifNull: [{ $arrayElemAt: ["$repairStats.count", 0] }, 0] } } }, // 4. 按日期排序(可选,让结果更规整) { $sort: { year: 1, month: 1, day: 1 } } ]);
方案说明
- 生成日期序列:通过
$documents构造出查询区间内的每一天的year、month、day文档,确保所有日期都被覆盖。 - 左连接统计数据:用
$lookup将日期序列与维修集合的统计结果关联,只匹配对应日期的维修记录。 - 补全零值:用
$ifNull和$arrayElemAt提取统计结果的count,若没有匹配结果则设为0。 - 排序(可选):最后按日期升序排列,让结果更符合阅读习惯。
内容的提问来源于stack exchange,提问作者Ali Parlatti
相关产品推荐
相关产品推荐

