MongoDB Shell中按天分组并按class字段统计指定月份每日数据的实现咨询
问题分析与解决方案
你的聚合查询有几个关键问题,导致没法得到预期结果:
1. 丢失了关键统计字段meta.class
在第一个$project阶段,你只提取了年、月、日信息,完全丢掉了meta.class字段——后面的分组阶段根本没有这个字段可以用来做分类统计,这是核心问题。
2. 分组逻辑顺序颠倒
你先按日期分组统计总数量,再试图按class分组,这和你想要的「先按日期+class统计单类数量,再把同日期的各类统计结果聚合到一起」的逻辑完全相反。
3. 语法小瑕疵
$match阶段末尾多了个冗余逗号,虽然MongoDB可能兼容,但属于不规范写法;另外用$$NOW结合$subtract来计算日期,比直接用ISODate().getTime()更符合MongoDB聚合的规范。
修改后的正确聚合查询
db.collection.aggregate([ // 筛选近30天的记录,用$$NOW更简洁规范 { $match: { "created_at": { $gt: { $subtract: [$$NOW, 1000*60*60*24*30] } } } }, // 提取日期字段,同时保留meta.class(绝对不能丢!) { $project: { "year": { $year: "$created_at" }, "month": { $month: "$created_at" }, "day": { $dayOfMonth: "$created_at" }, "class": "$meta.class" } }, // 第一级分组:按「日期+class」组合分组,统计单日期单class的数量 { $group: { _id: { year: "$year", month: "$month", day: "$day", class: "$class" }, total: { $sum: 1 } } }, // 第二级分组:按日期分组,把同日期的class统计结果整理成数组 { $group: { _id: { year: "$_id.year", month: "$_id.month", day: "$_id.day" }, count: { $push: { _id: "$_id.class", total: "$total" } } } }, // 可选:按日期排序,让结果更整齐易读 { $sort: { "_id.year": 1, "_id.month": 1, "_id.day": 1 } } ])
逻辑拆解说明
$match阶段:用聚合内置变量$$NOW替代手动生成日期,语法更规范,精准筛选近30天的记录。$project阶段:在提取日期字段的同时,必须保留meta.class,这是后续分类统计的核心依据。- 第一级
$group:先按「日期+class」的组合维度分组,得到每个日期下每个class的独立统计结果,比如{_id: {year:2021, month:10, day:1, class:"class1"}, total:2}。 - 第二级
$group:按日期维度合并结果,用$push把同日期的所有class统计项打包成count数组,完全匹配你想要的输出格式。 $sort阶段:可选但推荐,让结果按日期从小到大排序,避免输出顺序混乱。
用你提供的示例数据测试,这个查询会输出:
[ { "_id" : { "year" : 2021, "month" : 10, "day" : 1 }, "count" : [ { "_id" : "class1", "total" : 2 }, { "_id" : "class2", "total" : 1 } ] } ]
(注:你的示例数据里10月1日实际有3条记录:class1两条、class2一条,和你写的示例预期略有出入,应该是笔误~)
内容的提问来源于stack exchange,提问作者himan
相关产品推荐
相关产品推荐

