如何用MongoDB聚合统计最近7天按星期的购买量并输出指定格式?
实现星期为键的7日购买量统计
现有代码的核心问题
- 对Date类型字段直接用
$substrCP截取字符串,本质是把Date转成UTC时区的ISO字符串后硬截前10位做分组键,既没有处理业务时区的日期偏移问题,也完全没有提取星期维度的信息,最终分组结果按日期字符串聚合,和目标的星期名键的结构完全不匹配。 - 聚合输出默认是结果数组结构,无法直接生成要求的单对象格式。
调整后的完整聚合实现
按照目标输出格式,需要在原有管道基础上补充日期属性提取、键值映射、结构转换三个核心环节,完整可运行的代码如下:
// 按业务实际时区调整值,东八区对应"Asia/Shanghai" const TIMEZONE = "Asia/Shanghai"; // 对应$dayOfWeek的返回值做映射:MongoDB中$dayOfWeek返回1=周日、2=周一...7=周六 const WEEK_NAME_LIST = [ "", // 占位空值,匹配从1开始的返回值下标 "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ]; Purchase.aggregate([ // 筛选最近7天的有效购买记录 { $match: { date: { $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), $lte: new Date() } } }, // 按星期维度分组统计购买量 { $group: { _id: { // 提取对应时区下的星期数字,避免UTC时区导致的跨天错配 weekDayNum: { $dayOfWeek: { date: "$date", timezone: TIMEZONE } } }, count: { $sum: 1 } } }, // 将数字星期值映射为目标格式的键名 { $project: { _id: 0, k: { $arrayElemAt: [WEEK_NAME_LIST, "$_id.weekDayNum"] }, v: "$count" } }, // 收集所有分组统计结果为数组 { $group: { _id: null, weekStatList: { $push: "$$ROOT" } } }, // 将数组转换为单对象,同时补全无购买记录的星期值为0 { $project: { _id: 0, statResult: { $arrayToObject: { $map: { input: WEEK_NAME_LIST.slice(1), // 去掉前面的空占位 as: "weekName", in: { k: "$$weekName", v: { $ifNull: [ { $first: { $filter: { input: "$weekStatList", as: "item", cond: { $eq: ["$$item.k", "$$weekName"] } } } }, 0 ] } } } } } } }, // 将统计结果提升为根文档,得到最终要求的对象结构 { $replaceRoot: { newRoot: "$statResult" } } ])
注意事项
- 必须给
$dayOfWeek配置业务对应的时区参数,否则会默认按UTC时间计算星期,出现时区偏移导致的统计错配,比如东八区凌晨的订单会被归到前一天的星期维度。 - 最终输出会自动补全没有购买记录的星期对应值为0,不会出现缺键的情况,完全匹配示例的格式要求。
- 注意MongoDB的
$dayOfWeek返回值规则:1对应周日,2对应周一,依次到7对应周六,不要搞混映射顺序导致键名错位。
内容的提问来源于stack exchange,提问作者Hani Ben Gozi
相关产品推荐
相关产品推荐

