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

MongoDB中用$lookup关联多集合统计指定用户各聊天室未读消息数

解决方案:关联三集合并统计指定用户未读消息数

针对你的需求,我们可以通过MongoDB的**多阶段聚合+多次$lookup**来实现,既关联三个集合,又精准统计指定用户的未读消息数,同时返回聊天室和参与者的完整信息。下面是针对用户ben的具体实现:

完整聚合查询代码

db.chatRooms.aggregate([
  // 第一步:关联chatMessages,统计当前用户的未读消息数
  {
    $lookup: {
      from: "chatMessages",
      let: { roomId: "$_id" },
      pipeline: [
        {
          $match: {
            $expr: {
              $and: [
                { $eq: ["$chatRoomId", "$$roomId"] },
                // 筛选出当前用户(ben)未读的消息
                { $in: [{ userId: "ben", status: false }, "$readStatus"] }
              ]
            }
          }
        },
        { $count: "unreadCount" }
      ],
      as: "unreadInfo"
    }
  },
  // 把统计的未读数量提取为单个字段(避免数组形式)
  {
    $addFields: {
      unreadCount: {
        $ifNull: [{ $arrayElemAt: ["$unreadInfo.unreadCount", 0] }, 0]
      }
    }
  },
  // 移除临时的unreadInfo数组
  { $project: { unreadInfo: 0 } },
  // 第二步:关联userDetails,获取所有参与者的完整详情
  {
    $lookup: {
      from: "userDetails",
      localField: "participants",
      foreignField: "_id",
      as: "participantDetails"
    }
  },
  // 可选:如果不需要保留原participants字段可以开启这一步
  // { $project: { participants: 0 } }
])

代码逐阶段解释

  1. 第一阶段$lookup(关联消息集合):

    • 用let定义当前聊天室的ID变量roomId,在子管道里匹配对应聊天室的消息
    • 核心筛选逻辑:精准匹配readStatus数组中包含{ userId: "ben", status: false }的消息,也就是ben标记为未读的内容
    • 用$count统计符合条件的消息数量,结果存入unreadInfo数组
  2. 提取未读数字段:

    • 用$arrayElemAt把数组里的统计值取出来,再用$ifNull处理无未读消息的场景(默认设为0,避免返回null)
  3. 第二阶段$lookup(关联用户详情集合):

    • 基于聊天室的participants字段(假设该字段存储用户ID数组),关联userDetails集合,把参与者的完整信息存入participantDetails数组

输出示例(符合期望格式)

[
  {
    "_id": "room_001",
    "roomName": "技术交流群",
    "createdAt": ISODate("2024-01-01T00:00:00Z"),
    "unreadCount": 3,
    "participantDetails": [
      {
        "_id": "ben",
        "username": "Ben",
        "avatar": "https://example.com/ben.jpg",
        "email": "ben@example.com"
      },
      {
        "_id": "alice",
        "username": "Alice",
        "avatar": "https://example.com/alice.jpg",
        "email": "alice@example.com"
      }
    ]
  },
  {
    "_id": "room_002",
    "roomName": "生活闲聊群",
    "createdAt": ISODate("2024-01-02T00:00:00Z"),
    "unreadCount": 0,
    "participantDetails": [
      {
        "_id": "ben",
        "username": "Ben",
        "avatar": "https://example.com/ben.jpg",
        "email": "ben@example.com"
      },
      {
        "_id": "charlie",
        "username": "Charlie",
        "avatar": "https://example.com/charlie.jpg",
        "email": "charlie@example.com"
      }
    ]
  }
]

注意事项

  • 确保chatMessages的readStatus字段是数组类型,每个元素为{ userId: String, status: Boolean }的结构
  • 如果你的用户ID是ObjectId类型,需要把查询里的"ben"改成对应的ObjectId("ben的id值")格式
  • 可以根据实际业务需求调整$project阶段,灵活保留或移除不需要的字段

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:45:28