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

如何创建便于类型化反序列化的MongoDB聚合Lookup查询

MongoDB聚合Lookup结果结构调整方案

集合结构

PERSON 集合

{
    "_id":{"$oid":"64a20e0c65e828693428a8c1"},
    "username":"username_0"
}

CONTACT 集合

{
    "_id":{"$oid":"64a20e0c65e828693428a8c5"},
    "person_id":{"$oid":"64a20e0c65e828693428a8c1"}, // 关联Person的ID
    "data":"data_0"
}

MESSAGE 集合

{
    "_id":{"$oid":"64a278f583364c2f4f3c81c8"},
    "contact_receiver_id":{"$oid":"64a20e0c65e828693428a8c5"}, // 关联Contact的ID
    "contact_sender_id":{"$oid":"64a20e0c65e828693428a8d3"} // 关联Contact的ID
}

期望返回结果结构

{
  "person": { // 所有用户信息包裹在此字段中
      "_id": { "$oid": "64a20e0c65e828693428a8c1" },
      "username": "username_0"
  },
  "contacts": [
    {
      "contact": { // 所有联系人信息包裹在此字段中
          "_id": { "$oid": "64a20e0c65e828693428a8c5" },
          "person_id": { "$oid": "64a20e0c65e828693428a8c1" },
          "data": "data_0"
      },
      "messages": [
          {
            "_id": { "$oid": "64a20e0c65e828693428a8c5" },
            "contact_receiver_id":{"$oid":"64a20e0c65e828693428a8c1"},
            "contact_sender_id":{"$oid":"64a20e0c65e828693428a8d3"}
          }
          // 更多消息
      ]
    }
    // 更多联系人
  ]
}

当前问题

已实现嵌套Lookup,但返回结果未按要求包裹字段,当前返回结构如下:

{
  "_id": { "$oid": "64a20e0c65e828693428a8c1" },
  "username": "username_0",
  "contacts": [
    {
      "_id": { "$oid": "64a20e0c65e828693428a8c5" },
      "person_id": { "$oid": "64a20e0c65e828693428a8c1" },
      "data": "data_0",
      "messages": [
          {
            "_id": { "$oid": "64a20e0c65e828693428a8c5" },
            "contact_receiver_id":{"$oid":"64a20e0c65e828693428a8c1"},    
            "contact_sender_id":{"$oid":"64a20e0c65e828693428a8d3"}
          }
          // 更多消息
      ]
    }
    // 更多联系人
  ]
}

动机

使用Kotlin MongoDB驱动,希望直接用以下数据类序列化聚合结果,避免重复定义包含额外字段的领域对象:

@Serializable
data class DbContactsMessages(
    val contact: Contact,       // 领域类
    val messages: List<Message>
)

@Serializable
data class UserConctactsMessages(
    val person: Person,         // 领域类
    val contacts: List<DbContactsMessages>
)

解决方案

通过聚合管道中的$lookup嵌套管道、$project和$addFields来调整结果结构,具体聚合查询如下:

db.person.aggregate([
  // 匹配目标用户(可根据业务调整条件)
  {
    $match: {
      _id: ObjectId("64a20e0c65e828693428a8c1")
    }
  },
  // 关联Contact集合并处理嵌套结构
  {
    $lookup: {
      from: "contact",
      localField: "_id",
      foreignField: "person_id",
      as: "contacts",
      pipeline: [
        // 关联当前联系人的所有消息
        {
          $lookup: {
            from: "message",
            let: { contactId: "$_id" },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $or: [
                      { $eq: ["$contact_receiver_id", "$$contactId"] },
                      { $eq: ["$contact_sender_id", "$$contactId"] }
                    ]
                  }
                }
              }
            ],
            as: "messages"
          }
        },
        // 将联系人信息包裹到contact字段
        {
          $project: {
            contact: "$$ROOT",
            messages: 1
          }
        },
        // 移除contact字段中冗余的messages属性
        {
          $addFields: {
            "contact.messages": "$$REMOVE"
          }
        }
      ]
    }
  },
  // 将用户信息包裹到person字段
  {
    $project: {
      person: "$$ROOT",
      contacts: 1
    }
  },
  // 移除person字段中冗余的contacts属性
  {
    $addFields: {
      "person.contacts": "$$REMOVE"
    }
  }
])

关键步骤说明

  1. 用户匹配:$match阶段定位目标用户,可根据业务需求修改匹配条件。
  2. 联系人与消息关联:
    • 在关联Contact的$lookup中嵌套管道,先通过$lookup获取该联系人的所有消息。
    • 用$project将当前联系人的完整数据($$ROOT)放入contact字段,同时保留messages列表。
    • 通过$addFields移除contact字段中自动带出的冗余messages属性。
  3. 用户结构调整:
    • 用$project将用户的完整数据放入person字段,保留contacts列表。
    • 再次使用$addFields移除person字段中冗余的contacts属性。

执行以上聚合查询后,返回结果将完全匹配你定义的Kotlin数据类结构,无需额外定义重复的领域对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 00:52:39