MongoDB如何按收发双方分组并筛选最大日期文档?
解决MongoDB按收发双方双向分组并取最新文档的问题
这问题挺典型的,要实现按收发双方双向分组(A-B和B-A归为同一组),同时取每组最新文档、统计组内数量,核心是先生成统一的会话标识,再通过MongoDB的聚合管道一步步处理。我给你详细拆解实现步骤:
1. 生成统一会话ID
首先要把收发双方的标识(sender/recipient)按固定顺序排序后拼接,确保A-B和B-A得到同一个会话ID。用$sortArray排序、$join拼接:
{ $addFields: { sessionId: { $join: [ { $sortArray: { input: ["$sender", "$recipient"], sortBy: 1 } }, "" ] } } }
这一步会给每个文档新增一个sessionId字段,比如sender=B、recipient=A的文档,sessionId会是"AB",和sender=A、recipient=B的文档保持一致。
2. 按会话ID分组统计
接下来按sessionId分组,统计组内文档数量、记录最大日期,同时把组内所有文档暂存到数组中:
{ $group: { _id: "$sessionId", count: { $sum: 1 }, maxDate: { $max: "$date" }, messages: { $push: "$$ROOT" } } }
count: { $sum: 1 }:统计每组的文档总数maxDate: { $max: "$date" }:记录组内最大的日期值messages: { $push: "$$ROOT" }:把组内所有原始文档(包含新增的sessionId)存入数组,方便后续筛选最新文档
3. 筛选出组内最新文档
从messages数组中过滤出日期等于maxDate的文档,用$arrayElemAt取第一个(如果有多个同最大日期的文档,可根据需求调整逻辑):
{ $addFields: { latestMessage: { $arrayElemAt: [ { $filter: { input: "$messages", cond: { $eq: ["$$this.date", "$maxDate"] } } }, 0 ] } } }
4. 整理最终输出结构
把筛选出的最新文档作为根文档,同时合并会话ID(作为_id)和统计数量count:
{ $replaceRoot: { newRoot: { $mergeObjects: [ "$latestMessage", { _id: "$_id", count: "$count" } ] } } }
5. (可选)移除冗余字段
如果不需要保留临时的sessionId字段,用$project移除:
{ $project: { sessionId: 0 } }
完整聚合管道代码
把以上步骤组合起来,完整的聚合查询如下:
db.yourCollectionName.aggregate([ // 步骤1:生成统一会话ID { $addFields: { sessionId: { $join: [ { $sortArray: { input: ["$sender", "$recipient"], sortBy: 1 } }, "" ] } } }, // 步骤2:按会话ID分组统计 { $group: { _id: "$sessionId", count: { $sum: 1 }, maxDate: { $max: "$date" }, messages: { $push: "$$ROOT" } } }, // 步骤3:筛选最新文档 { $addFields: { latestMessage: { $arrayElemAt: [ { $filter: { input: "$messages", cond: { $eq: ["$$this.date", "$maxDate"] } } }, 0 ] } } }, // 步骤4:整理输出结构 { $replaceRoot: { newRoot: { $mergeObjects: [ "$latestMessage", { _id: "$_id", count: "$count" } ] } } }, // 步骤5:移除冗余字段 { $project: { sessionId: 0 } } ])
验证结果
运行这个聚合后,会得到你期望的结果:
[ { _id: 'AB', sender: 'B', recipient: 'A', date: 2, contents: 'Blablabla2', count: 2 }, { _id: 'AC', sender: 'A', recipient: 'C', date: 4, contents: 'Blablabla1', count: 2 }, { _id: 'CD', sender: 'C', recipient: 'D', date: 3, contents: 'Blablabla2', count: 1 } ]
内容的提问来源于stack exchange,提问作者nelkor
相关产品推荐
相关产品推荐

