MongoDB聚合查询:关联两个集合并获取子集合指定字段
MongoDB聚合实现Contacts与Companies关联查询
问题背景
需要将Contacts集合与Companies集合通过companyId关联,把Companies中的companyName和domain字段合并到对应的Contact文档中,替代原有的TypeScript循环逻辑。
集合结构
Contacts集合示例
{ _id: ObjectId('2341908342'), fName: "Test", lName: "TestTwo", companyId: "61f4147b8415abfc04d09207", orgId: ObjectId("xxx") }, { _id: ObjectId('2341908343'), fName: "John", lName: "Doe", companyId: "62847a16ee5c5047820415d2", orgId: ObjectId("xxx") }
Companies集合示例
{ _id: ObjectId('61f4147b8415abfc04d09207'), companyName: "Facebook", domain: "www.facebook.com", }, { _id: ObjectId('62847a16ee5c5047820415d2'), companyName: "Google", domain: "www.Google.com", }
期望输出
{ _id: ObjectId('2341908342'), fName: "Test", lName: "TestTwo", companyId: "61f4147b8415abfc04d09207", companyName: "Facebook", domain: "www.facebook.com", orgId: ObjectId("xxx") }
原代码错误分析
你提供的聚合代码存在以下问题:
let语句错误:取了Contacts的_id转换为字符串,而应该取companyId字段- 匹配条件错误:Companies集合中没有
companyId字段,应该用$_id转换为字符串后与Contacts的companyId匹配 as字段命名不合理:lookup结果是数组,应该用类似companyInfo的名称,而非companyName
正确聚合实现方案
以下是与原TypeScript逻辑完全一致的聚合代码:
try { const allContacts = await contactsCollection.aggregate([ // 过滤当前组织的联系人 { $match: { orgId: new ObjectId(req.user.orgId) } }, // 关联Companies集合,匹配对应公司 { $lookup: { from: "companies", let: { contactCompanyId: "$companyId" }, // 引用当前Contact的companyId pipeline: [ { $match: { $expr: { // 将Companies的_id转为字符串,与Contact的companyId(字符串)匹配 $eq: [{ $toString: "$_id" }, "$$contactCompanyId"] } } }, // 仅返回需要的字段,减少数据量 { $project: { _id: 0, companyName: 1, domain: 1 } } ], as: "companyInfo" // 关联结果存入数组 } }, // 展开关联数组,保留无匹配公司的联系人 { $unwind: { path: "$companyInfo", preserveNullAndEmptyArrays: true } }, // 将公司字段合并到根文档 { $addFields: { companyName: "$companyInfo.companyName", domain: "$companyInfo.domain" } }, // 移除临时的companyInfo字段(可选) { $project: { companyInfo: 0 } } ]).toArray(); } catch (err) { console.error('聚合查询失败:', err); }
代码逻辑说明
- $match:过滤出当前用户所属组织的联系人
- $lookup:关联Companies集合,通过转换字段类型实现字符串
companyId与ObjectId_id的匹配,同时仅返回需要的companyName和domain字段 - $unwind:展开关联结果数组,
preserveNullAndEmptyArrays: true确保即使没有匹配到公司,联系人文档也会被保留 - $addFields:将关联得到的公司字段合并到联系人根文档中
- $project:移除临时的
companyInfo数组字段,使输出结构更简洁
内容的提问来源于stack exchange,提问作者CameronWood23
相关产品推荐
相关产品推荐

