MongoDB聊天应用聚合查询问题:获取单对话最新消息
解决聊天应用中获取单对话最新消息的聚合查询问题
问题原因
你的聚合查询返回两条消息,是因为分组时用{sender: "$sender", receiver: "$receiver"}作为_id,这会把UserX→UserY和UserY→UserX当成两个独立对话分组,但实际上这属于同一个双向对话。
解决方案
核心是给每个对话生成唯一统一标识:将对话涉及的两个用户ID按固定顺序(比如ObjectId字符串排序)组成数组作为分组键,这样无论消息发送方向如何,同个对话都会被归为一组。
修改后的聚合查询代码
const Chats = await Message.aggregate([ // 过滤当前用户参与的所有消息 { $match: { $or: [ { sender: mongoose.Types.ObjectId(req.user.id) }, { receiver: mongoose.Types.ObjectId(req.user.id) } ] } }, // 生成对话统一标识:将两个用户ID排序后组成数组 { $addFields: { conversationKey: { $sortArray: { input: ["$sender", "$receiver"], sortBy: 1 } } } }, // 按创建时间倒序,确保最新消息排在最前 { $sort: { createdAt: -1 } }, // 按统一标识分组,取每组第一条(最新)消息 { $group: { _id: "$conversationKey", latestMessage: { $first: "$$ROOT" } } }, // 替换为最新消息内容 { $replaceWith: "$latestMessage" }, // 可选:关联用户信息,方便前端展示聊天对象详情 { $lookup: { from: "users", localField: "sender", foreignField: "_id", as: "senderInfo" } }, { $lookup: { from: "users", localField: "receiver", foreignField: "_id", as: "receiverInfo" } }, { $unwind: "$senderInfo" }, { $unwind: "$receiverInfo" } ]);
代码说明
- $match阶段:直接通过
sender或receiver匹配当前用户,比依赖usersRelated更可靠,避免数组维护错误。 - $addFields阶段:用
$sortArray对两个用户ID排序,生成统一的conversationKey,确保同对话的消息分组一致。 - $sort阶段:按
createdAt倒序,保证分组后$first能取到最新消息。 - $group阶段:以
conversationKey为分组键,用$first获取每组的第一条(最新)消息。 - 可选$lookup阶段:关联用户集合,获取发送方/接收方的昵称、头像等信息,满足前端展示需求。
效果验证
针对你提供的两条测试消息,执行上述查询后,两条消息会被归为同一组,最终返回"Good thanks, and you?"这条最新消息,符合主流聊天应用的聊天列表展示逻辑。
内容的提问来源于stack exchange,提问作者Technosha
相关产品推荐
相关产品推荐

