You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Node.js与Mongoose的聊天应用会话聚合查询问题

解决方案:用Mongoose聚合查询会话列表

针对你需要的登录用户会话列表(包含最新消息和聊天对象用户名),可以通过Mongoose的聚合管道实现,核心是按用户对分组并提取每组最新消息,同时关联用户信息获取聊天对象名称。

假设当前登录用户的ID为currentUserId(需替换为实际的用户ID变量),以下是完整的聚合查询代码:

const currentUserId = "用户的ObjectId"; // 替换为实际登录用户ID

const conversations = await Message.aggregate([
  // 阶段1:筛选当前用户参与的所有消息
  {
    $match: {
      users: { $in: [currentUserId] } // 利用users字段匹配当前用户参与的会话
    }
  },
  // 阶段2:按会话分组(确保A-B和B-A属于同一个会话)
  {
    $group: {
      _id: {
        // 对users数组排序后生成唯一会话ID,避免重复分组
        $concat: [
          { $arrayElemAt: [{ $sortArray: { input: "$users", sortBy: 1 } }, 0] },
          "-",
          { $arrayElemAt: [{ $sortArray: { input: "$users", sortBy: 1 } }, 1] }
        ]
      },
      latestMessage: { $last: "$$ROOT" }, // 取每组最新的消息(依赖timestamps的createdAt排序)
      lastUpdated: { $max: "$createdAt" } // 记录会话最后更新时间
    }
  },
  // 阶段3:按最后更新时间倒序,让最新会话排在前面
  {
    $sort: { lastUpdated: -1 }
  },
  // 阶段4:关联User集合,获取聊天对象的信息
  {
    $lookup: {
      from: "users", // User集合的名称(注意是集合名,不是模型名)
      let: {
        fromId: "$latestMessage.fromUser",
        toId: "$latestMessage.toUser",
        currentId: currentUserId
      },
      pipeline: [
        {
          $match: {
            $expr: {
              // 匹配不是当前用户的那一方
              $or: [
                { $eq: ["$_id", "$$fromId"] },
                { $eq: ["$_id", "$$toId"] }
              ],
              $ne: ["$_id", "$$currentId"]
            }
          }
        },
        { $project: { username: 1, _id: 0 } } // 只取用户名字段
      ],
      as: "chatPartner"
    }
  },
  // 阶段5:整理输出格式
  {
    $project: {
      _id: 0,
      conversationId: "$_id", // 会话唯一ID
      latestMessage: {
        content: "$latestMessage.content",
        createdAt: "$latestMessage.createdAt",
        isRead: "$latestMessage.messageRead"
      },
      chatPartnerUsername: { $arrayElemAt: ["$chatPartner.username", 0] },
      lastUpdated: 1
    }
  }
]);

关键步骤说明:

  1. 筛选消息:通过users字段快速匹配当前用户参与的所有消息,比单独匹配fromUser/toUser更高效。
  2. 分组会话:对users数组排序后拼接成唯一ID,确保两个用户之间的会话不管谁发消息,都被分到同一组,解决“发送方/接收方身份互换”的分组问题。
  3. 提取最新消息:用$last直接取每组最后一条消息(Mongoose的timestamps会自动维护createdAt,聚合时默认按插入顺序处理,也可以先加$sort确保顺序)。
  4. 关联用户信息:通过$lookup的子管道,精准筛选出聊天对象(排除当前用户),只返回需要的username字段,减少数据传输。
  5. 格式化输出:整理成前端需要的结构,包含会话ID、最新消息内容、时间、是否已读,以及聊天对象用户名。

补充说明:

  • 如果你的users字段没有正确存储双方用户ID,需要先确保每条消息创建时,把fromUser和toUser存入users数组(比如users: [fromId, toId]),否则可以替换阶段1的$match为:
    $match: {
      $or: [
        { fromUser: currentUserId },
        { toUser: currentUserId }
      ]
    }
    
    同时阶段2的分组键改为基于fromUser和toUser的排序组合:
    _id: {
      $concat: [
        { $min: ["$fromUser", "$toUser"] },
        "-",
        { $max: ["$fromUser", "$toUser"] }
      ]
    }
    
  • 前端拿到conversations数组后,直接渲染列表即可,点击会话时用conversationId作为参数,查询该会话的所有消息。

内容的提问来源于stack exchange,提问作者user12504353

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 18:55:30