MongoDB聚合:按日期分组并按时间间隔拆分会话
MongoDB数组拆分与字段添加解决方案
针对你提出的需求,我们可以通过MongoDB的聚合管道分步实现,核心是先给数组元素标记分组,再拆分重组,最后添加目标字段:
完整聚合管道
[ // 步骤1:给数组元素计算与下一个元素的时间差 { $set: { visitedWithDiff: { $map: { input: { $range: [0, { $size: "$allVisitedPages" }] }, as: "idx", in: { $mergeObjects: [ { $arrayElemAt: ["$allVisitedPages", "$$idx"] }, { timeDiffMinutes: { $cond: { if: { $lt: [{ $add: ["$$idx", 1] }, { $size: "$allVisitedPages" }] }, then: { $datediff: { startDate: { $arrayElemAt: ["$allVisitedPages.visitTime", "$$idx"] }, endDate: { $arrayElemAt: ["$allVisitedPages.visitTime", { $add: ["$$idx", 1] }] }, unit: "minute" } }, else: null } } } ] } } } } }, // 步骤2:给元素标记分组ID(相邻时间差>120分钟则切换分组) { $set: { visitedWithGroup: { $reduce: { input: "$visitedWithDiff", initialValue: { currentGroup: 0, items: [] }, in: { currentGroup: { $cond: { if: { $and: ["$$this.timeDiffMinutes", { $gt: ["$$this.timeDiffMinutes", 120] }] }, then: { $add: ["$$value.currentGroup", 1] }, else: "$$value.currentGroup" } }, items: { $concatArrays: [ "$$value.items", [{ $mergeObjects: ["$$this", { groupId: "$$value.currentGroup" }] }] ] } } } } } }, // 步骤3:拆分数组并按分组重组为新文档 { $unwind: "$visitedWithGroup.items" }, { $group: { _id: { ip: "$_id.ip", date: "$_id.date", groupId: "$visitedWithGroup.items.groupId" }, allVisitedPages: { $push: "$visitedWithGroup.items" } } }, // 步骤4:给每个元素添加durationInMinutes字段 { $set: { allVisitedPages: { $map: { input: { $range: [0, { $size: "$allVisitedPages" }] }, as: "idx", in: { $mergeObjects: [ { $arrayElemAt: ["$allVisitedPages", "$$idx"] }, { durationInMinutes: { $cond: { if: { $eq: ["$$idx", { $subtract: [{ $size: "$allVisitedPages" }, 1] }] }, then: 2, else: { $arrayElemAt: ["$allVisitedPages.timeDiffMinutes", "$$idx"] } } } } ] } } } } }, // 步骤5:清理冗余字段,保留需要的内容 { $project: { _id: 0, ip: "$_id.ip", date: "$_id.date", allVisitedPages: { $map: { input: "$allVisitedPages", in: { visitTime: "$$this.visitTime", pageUrl: "$$this.pageUrl", // 这里替换成你需要保留的其他原始字段 durationInMinutes: "$$this.durationInMinutes" } } } } } ]
关键逻辑说明
- 时间差计算:用
$map遍历数组,通过$datediff计算当前元素与下一个元素的分钟级时间差,最后一个元素设为null - 分组标记:用
$reduce累计分组ID,当相邻时间差超过120分钟时,分组ID自增,确保同一连续会话的元素归属同一分组 - 拆分重组:通过
$unwind打散数组,再$group按IP、日期和分组ID重新聚合,得到拆分后的独立会话文档 - 字段添加:再次遍历数组,给最后一个元素固定设置
durationInMinutes:2,其他元素直接使用之前计算的时间差
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

