MongoDB新手求助:实现每日各状态文档计数,补全无数据日期零值结果
MongoDB每日分状态计数(含零值补全)实现方案
核心实现思路
MongoDB原生聚合默认只会输出存在文档的日期/状态组合,不会自动补全缺失的零值,因此整体方案拆为3步落地:
- 先根据现有文档的日期范围,生成覆盖全时间段的连续日期骨架
- 聚合现有文档,按「日期+状态」维度统计实际文档数
- 将日期骨架与实际统计结果做关联匹配,对没有匹配到的日期、状态项自动补计数0
可直接运行的聚合查询
注意:执行前请把代码中两处your_collection_name替换为你实际的业务集合名称
db.your_collection_name.aggregate([ // 提取集合内所有日期的边界范围,作为后续生成连续日期的依据 { $group: { _id: null, minDate: { $min: "$date" }, maxDate: { $max: "$date" } } }, // 生成连续日期骨架,适配示例中"月/日"的字符串日期格式 { $project: { dateSkeleton: { $map: { input: { $range: [ // 解析字符串里的月份数值作为序列起点 { $toInt: { $first: { $split: ["$minDate", "/"] } } }, // 解析字符串里的月份数值作为序列终点,+1是因为$range左闭右开 { $add: [ { $toInt: { $first: { $split: ["$maxDate", "/"] } } }, 1 ] } ] }, as: "monthItem", // 拼接成和原始格式一致的日期字符串 in: { $concat: [ { $toString: "$$monthItem" }, "/1" ] } } } } }, // 把数组形式的日期骨架拆成单条日期记录,作为后续补零的基准 { $unwind: "$dateSkeleton" }, { $project: { _id: 0, date: "$dateSkeleton" } }, // 关联实际文档的分状态统计结果 { $lookup: { from: "your_collection_name", pipeline: [ // 第一层分组:按日期+状态维度统计实际文档数量 { $group: { _id: { date: "$date", status: "$status" }, count: { $sum: 1 } } }, // 第二层分组:按日期把所有状态的计数聚合成映射表 { $group: { _id: "$_id.date", statusCountMap: { $push: { k: "$_id.status", v: "$count" } } } } ], as: "statResult" } }, // 匹配对应状态的计数,无匹配则补0 { $project: { _id: 0, date: 1, statusA: { $ifNull: [ { $getField: { field: "v", input: { $arrayElemAt: [ "$statResult.statusCountMap", { $indexOfArray: [ "$statResult.statusCountMap.k", "A" ] } ] } } }, 0 ] }, statusB: { $ifNull: [ { $getField: { field: "v", input: { $arrayElemAt: [ "$statResult.statusCountMap", { $indexOfArray: [ "$statResult.statusCountMap.k", "B" ] } ] } } }, 0 ] } } } ])
按需调整指引
- 状态枚举扩展:如果业务中除了A、B还有其他固定状态,只需在最后
$project阶段照着statusA、statusB的写法,新增对应状态的取值判断即可,没有匹配到的会自动补0 - 日期格式适配:如果你的date字段不是示例里的短字符串格式,而是标准ISODate类型,把生成连续日期骨架的逻辑替换为:先用
$dateDiff计算最早和最晚日期的天数差,再通过$dateAdd逐天生成连续的标准日期对象即可 - 自定义时间范围:如果不需要自动识别集合内的日期范围,而是要固定统计某段时间(比如1月整月),可以跳过第一步提取min/max日期的逻辑,直接在生成日期骨架的
$range阶段传入你指定的时间边界值
内容的提问来源于stack exchange,提问作者ring
相关产品推荐
相关产品推荐

