MongoDB聚合框架下按visitor_id和签到日期合并多对象实现方案咨询
MongoDB聚合管道合并同访客同日期打卡记录方案
实现思路
核心是按visitor_id + clocked_in_at的日期维度分组,利用原始数据已按clocked_in_at升序排列的特性,分组时用$first取分组内第一条的clocked_in_at和原始_id,用$last取分组内最后一条的clocked_out_at即可满足需求。
具体实现
场景1:待处理数据是独立的文档流
db.collection.aggregate([ // 1. 提取打卡日期作为分组依据,如果clocked_in_at是字符串类型,先转ISODate:date: { $toDate: "$clocked_in_at" } { $addFields: { clocked_in_date: { $dateToString: { format: "%Y-%m-%d", date: "$clocked_in_at" } } } }, // 2. 按访客ID+打卡日期分组 { $group: { _id: { visitor_id: "$visitor_id", date: "$clocked_in_date" }, original_id: { $first: "$_id" }, clocked_in_at: { $first: "$clocked_in_at" }, clocked_out_at: { $last: "$clocked_out_at" } } }, // 3. 调整字段结构为目标格式 { $project: { _id: "$original_id", visitor_id: "$_id.visitor_id", clocked_in_at: 1, clocked_out_at: 1 } }, // 可选:恢复原始打卡时间排序 { $sort: { clocked_in_at: 1 } } ])
场景2:待处理数组是某文档的嵌套字段(假设字段名为records)
db.collection.aggregate([ // 拆分嵌套数组 { $unwind: "$records" }, // 提取打卡日期 { $addFields: { "records.clocked_in_date": { $dateToString: { format: "%Y-%m-%d", date: "$records.clocked_in_at" } } } }, // 按原文档ID、访客ID、打卡日期分组 { $group: { _id: { root_id: "$_id", visitor_id: "$records.visitor_id", date: "$records.clocked_in_date" }, original_record_id: { $first: "$records._id" }, clocked_in_at: { $first: "$records.clocked_in_at" }, clocked_out_at: { $last: "$records.clocked_out_at" } } }, // 重新组装为嵌套数组 { $group: { _id: "$_id.root_id", records: { $push: { _id: "$original_record_id", visitor_id: "$_id.visitor_id", clocked_in_at: "$clocked_in_at", clocked_out_at: "$clocked_out_at" } } } }, // 可选:给嵌套数组按打卡时间排序 { $addFields: { records: { $sortArray: { input: "$records", sortBy: { clocked_in_at: 1 } } } } } ])
常见问题说明
之前用$mergeObjects达不到预期是因为该算子会全量合并同名字段,默认用后出现的字段值覆盖先出现的,而你需要的是指定取分组首尾的特定字段,直接在$group阶段用$first和$last取值更贴合需求。
内容的提问来源于stack exchange,提问作者Scoopa
相关产品推荐
相关产品推荐

