如何通过MongoDB聚合将数组中rentalContractId关联至详情表
MongoDB数组内关联查询的正确实现方式
问题场景
需要从rentalcontractdetails集合中查询product文档里productPrice数组中每个对象的rentalContractId对应的详情数据。
product文档结构:
{ "productPrice": [ { "rentalContractId": ObjectId("64018dfc8a7d37f378d6c427"), "price": 20 }, { "rentalContractId": ObjectId("64018dfc8a7d37f378d6c426"), "price": 10 } ] }
错误尝试及问题
使用以下聚合语句时,每个productPrice对象都会包含所有关联的rentalcontractdetails数据,不符合预期:
db.products.aggregate([ { $lookup: { from: "rentalcontractdetails", localField: "productPrice.rentalContractId", foreignField: "_id", as: "rentalContract", }, }, { $set: { "productPrice.rentalContract": "$rentalContract" }}])
错误输出示例:
[ { "rentalContractId": "64018dfc8a7d37f378d6c427", "price": 20, "rentalContract": [ { "_id": "64018dfc8a7d37f378d6c427", "contractType": "8 - Days Rental", "contractDaysInNumber": 8, "createdAt": "2023-03-03T06:04:44.531Z", "isDeleted": false, "isArchived": false }, { "_id": "64018dfc8a7d37f378d6c426", "contractType": "4 - Days Rental", "contractDaysInNumber": 4, "createdAt": "2023-03-03T06:04:44.531Z", "isDeleted": false, "isArchived": false } ] }, { "rentalContractId": "64018dfc8a7d37f378d6c426", "price": 10, "rentalContract": [ { "_id": "64018dfc8a7d37f378d6c427", "contractType": "8 - Days Rental", "contractDaysInNumber": 8, "createdAt": "2023-03-03T06:04:44.531Z", "isDeleted": false, "isArchived": false }, { "_id": "64018dfc8a7d37f378d6c426", "contractType": "4 - Days Rental", "contractDaysInNumber": 4, "createdAt": "2023-03-03T06:04:44.531Z", "isDeleted": false, "isArchived": false } ] } ]
期望输出
将每个rentalContractId替换为对应的详情数据:
{ "productPrice": [ { "rentalContractId": { "_id": "64018dfc8a7d37f378d6c427", "contractType": "8 - Days Rental", }, "price": 20 }, { "rentalContractId": { "_id": "64018dfc8a7d37f378d6c426", "contractType": "4 - Days Rental", }, "price": 10 } ] }
解决方案
需要先拆分productPrice数组,再逐个关联,最后重组数组。正确的聚合语句如下:
db.products.aggregate([ // 拆分productPrice数组为单个文档 { $unwind: "$productPrice" }, // 关联rentalcontractdetails集合,匹配对应的_id { $lookup: { from: "rentalcontractdetails", localField: "productPrice.rentalContractId", foreignField: "_id", as: "productPrice.rentalContractId" } }, // 将关联后的数组转为单个对象(因为lookup返回数组,这里取第一个元素) { $set: { "productPrice.rentalContractId": { $first: "$productPrice.rentalContractId" } } }, // 重新组合productPrice数组 { $group: { _id: "$_id", productPrice: { $push: "$productPrice" }, // 如果需要保留其他字段,在这里添加,比如: // otherField: { $first: "$otherField" } } } ])
代码说明
$unwind:将productPrice数组拆分为独立的文档,每个文档对应数组中的一个元素,确保后续的$lookup可以逐个匹配。$lookup:此时localField指向单个productPrice.rentalContractId,关联后会将匹配的详情存入productPrice.rentalContractId数组。$set+$first:因为$lookup返回的是数组(即使只有一个匹配项),用$first取出数组中的第一个元素,替换原来的rentalContractId值。$group:将拆分后的文档重新组合为原结构,把每个productPrice元素推回数组,同时保留原文档的_id和其他需要的字段。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

