MongoDB按日期分组聚合如何补全查询范围内的缺失日期
解决方案
可以通过调整聚合管道实现目标,针对MongoDB不同版本有两种实现方式:
方案1:MongoDB 5.1及以上版本(推荐)
使用MongoDB原生提供的$densify聚合操作符,它可以自动补全指定范围内的缺失序列,你可以直接在原有管道基础上追加$densify阶段,调整后的完整管道如下:
[ { $match: { orderDate: { $gte: new Date('2021-08-01T00:00:00+05:30'), $lte: new Date('2021-08-06T23:59:59+05:30'), } } }, { $group: { _id: { $dateToString: { format: '%Y-%m-%d', date: '$orderDate', } } } }, // 新增:补全缺失的日期 { $densify: { field: "_id", range: { step: 1, unit: "day", bounds: [ ISODate("2021-08-01T00:00:00+05:30"), ISODate("2021-08-06T23:59:59+05:30") ] } } }, // 可选:如果需要统一把_id转为字符串格式,可以加这个阶段 { $project: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$_id" } } } } ]
运行后就会自动补全2021-08-03、2021-08-06两条记录,如果你还有其他统计字段(比如订单数、销售额等),缺失的日期对应的统计字段会自动赋值为null,你可以后续追加$addFields阶段把null替换为0。
方案2:MongoDB 5.0及以下版本
如果你的MongoDB版本不支持$densify,可以用$documents配合$dateDiff生成完整的日期序列,再和原有分组结果做左连接,实现逻辑如下:
[ // 第一步:先做原有分组统计 { $match: { orderDate: { $gte: new Date('2021-08-01T00:00:00+05:30'), $lte: new Date('2021-08-06T23:59:59+05:30'), } } }, { $group: { _id: { $dateToString: { format: '%Y-%m-%d', date: '$orderDate', } } } }, // 第二步:把分组结果合并为一个数组 { $group: { _id: null, existDates: { $push: "$$ROOT" } } }, // 第三步:生成查询范围内的完整日期序列 { $addFields: { allDates: { $map: { input: { $range: [0, 6] }, // 6是日期跨度的天数,这里从8.1到8.6共6天 in: { _id: { $dateToString: { format: "%Y-%m-%d", date: { $add: [ new Date('2021-08-01T00:00:00+05:30'), { $multiply: ["$$this", 86400000] } ] } } } } } } } }, // 第四步:合并两个数组,去重 { $project: { result: { $setUnion: ["$allDates", "$existDates"] } } }, // 第五步:拆分数组返回单条记录 { $unwind: "$result" }, { $replaceRoot: { newRoot: "$result" } } ]
注意:这个方案里$range的第二个参数需要你根据传入的日期范围动态计算总天数,也可以用$dateDiff动态计算,避免硬编码。
内容的提问来源于stack exchange,提问作者Mohamed Imran
相关产品推荐
相关产品推荐

