$ dateToString 的 format 必须用 %Y-%m 而不是 YYYY-MM,因其仅支持 C 语言风格格式符;%Y 表示 4 位年份,%m 表示补零月份,错误写法会导致字符串原样输出、分组失效。
$dateToString 的 format 里必须用 %Y-%m 而不是 YYYY-MM
因为 $dateToString 不支持 ISO 标准的 YYYY-MM 写法,只认 C 语言风格的格式符。写成 "%Y-%m" 才能正确提取年月,写成 "YYYY-MM" 会原样输出字符串,导致分组失效。
常见错误现象:{"year_month": "YYYY-MM"} —— 所有文档都归到同一个假分组里。
%Y 是 4 位完整年份(如 2024),%y 是 2 位(24),别混用%m 是补零月份数字(01),%-m 在 MongoDB 不支持(Linux 风格去零不生效)Date 类型,先用 $dateFromString 转换,否则 $dateToString 直接报错或返回 null
created_at 字段按月分组不能直接对原始时间戳分组,必须先标准化为“年月字符串”,再 $group。关键在于处理空值、非法日期和时区。
$cond 判断 created_at 是否有效,避免 null 进入分组干扰计数timezone 参数(如 "Asia/Shanghai"),否则默认 UTC,可能让北京时间凌晨的数据被算进上个月{ $group: { _id: { $dateToString: { format: "%Y-%m", date: "$created_at", timezone: "Asia/Shanghai" } }, count: { $sum: 1 } }}
分组后的 _id 是字符串(如 "2024-03"),按字典序排序就等价于按时间先后,但要注意:如果跨年(如 "2023-12" 和 "2024-01"),字典序依然正确;但如果格式是 "%m-%Y" 就会乱序。
{$sort: {_id: 1}} + {$limit: 6}
{$sort: {_id: -1}} + {$limit: 6}
$group,再 $sort,最后 $limit —— 顺序不能颠倒,否则 $limit 会截断未分组的原始文档$dateToString 分组有没有加速作用没有。MongoDB 无法使用普通日期索引优化 $dateToString 表达式,因为这是运行时计算字段。但你可以提前建好“年月”冗余字段并索引它。
year_month: "2024-03" 字段,并对它建索引:db.collection.createIndex({year_month: 1})
真正难的不是写出能跑的聚合语句,而是确保时间字段类型干净、时区意识明确、以及面对百万级数据时,敢不敢把计算逻辑从查询层移到写入层。