如何使用MongoDB Aggregate创建活跃客户时间序列?
MongoDB聚合实现跨期活跃客户的月度时间序列统计
要解决单个客户在多个月份被统计的问题,核心思路是把每个客户的活跃期拆分成对应每个月份的独立记录,再按年月分组统计。以下是完整可运行的聚合管道:
完整聚合代码
db.customers.aggregate([ // 1. 统一处理活跃结束日期:未结束的客户用当前日期替代 { $addFields: { effectiveEndDate: { $ifNull: ["$activeEndDate", new Date()] } } }, // 2. 提取活跃起始/结束的年月对象,计算活跃总月数 { $addFields: { startYM: { year: { $year: "$activeStartDate" }, month: { $month: "$activeStartDate" } }, endYM: { year: { $year: "$effectiveEndDate" }, month: { $month: "$effectiveEndDate" } }, totalActiveMonths: { $add: [ { $multiply: [{ $subtract: ["$endYM.year", "$startYM.year"] }, 12] }, { $subtract: ["$endYM.month", "$startYM.month"] }, 1 // 包含起始月份本身 ] } } }, // 3. 生成月份偏移量数组,再展开成单条记录 { $addFields: { monthOffsets: { $range: [0, "$totalActiveMonths", 1] } } }, { $unwind: "$monthOffsets" }, // 4. 根据偏移量计算每个对应的年月 { $addFields: { currentYM: { $let: { vars: { // 把月份转成0基计算(1月→0),加上偏移量后再转回1基 totalMonths: { $add: ["$startYM.month", "$monthOffsets", -1] }, yearShift: { $floor: { $divide: ["$$totalMonths", 12] } }, targetMonth: { $add: [{ $mod: ["$$totalMonths", 12] }, 1] } }, in: { year: { $add: ["$startYM.year", "$$yearShift"] }, month: "$$targetMonth" } } } } }, // 5. 按年月分组统计活跃客户数 { $group: { _id: "$currentYM", activeCustomers: { $sum: 1 } } }, // 6. 按时间顺序排序 { $sort: { "_id.year": 1, "_id.month": 1 } } ])
核心逻辑拆解
- 统一活跃结束时间:用
$ifNull将未终止活跃(activeEndDate为null)的客户,结束时间设为当前日期,确保这类客户能被统计到最新月份。 - 拆分活跃期为单月记录:通过计算活跃总月数生成偏移量数组,再用
$unwind展开,让每个客户的每一个活跃月份都对应一条独立文档——这是实现“单个客户多分组统计”的关键。 - 计算对应年月:基于起始年月和偏移量,通过数学运算处理跨年跨月的边界情况,确保每个偏移量都能得到正确的年月值。
- 分组排序:按计算出的年月分组统计数量,最后排序得到有序的时间序列。
输出效果
最终会生成你需要的格式:
[ { "_id": { "year": 2022, "month": 7 }, "activeCustomers": 500 }, { "_id": { "year": 2022, "month": 8 }, "activeCustomers": 520 }, // ... 后续月份数据 ]
内容的提问来源于stack exchange,提问作者Jolle
相关产品推荐
相关产品推荐

