MongoDB $lookup 在 2 级嵌套文档上不使用 $unwind
Posted
技术标签:
【中文标题】MongoDB $lookup 在 2 级嵌套文档上不使用 $unwind【英文标题】:MongoDB $lookup on 2 level nested document without using $unwind 【发布时间】:2020-11-26 21:19:48 【问题描述】:我有以下文件
loanRequest(只写我想投影的键)
"_id": "5f2bf26783f65d33026ea592",
"lendingpartner":
/* some keys here */
,
"loans": [
"loanid": 43809,
"loanamount": 761256,
"jewels": [
"5f2bf26783f65d33026ea593",
"5f2bf26783f65d33026ea594"
"5f2bf26783f65d33026ea595"
],
]
质押珠宝
"_id": "5f2bf26783f65d33026ea593",
"netweight": 8.52,
"purity": 19,
我想要实现的是
"_id": "5f2bf2b583f65d33026ea603",
"lendingpartner":
/* some keys here */
,
"loans": [
"loanid": 40010,
"loanamount": 100000,
"jewels": [
"_id": "5f2bf26783f65d33026ea593",
"netweight": 8.52,
"purity": 19,
,
"_id": "5f2bf26783f65d33026ea594",
"netweight": 5.2,
"purity": 40,
,
"_id": "5f2bf26783f65d33026ea595",
"netweight": 4.52,
"purity": 39,
]
]
由于我希望将珠宝详细信息填充到每笔贷款的珠宝数组中,$unwind
不会帮助我。 (我试过用它做实验)
我以为我可以在贷款数组上运行$map
,然后为贷款的每个宝石运行$lookup
(双地图?),但无法提出可行的解决方案。
无论如何,这似乎不是正确的方法。
这是我能想到的最好的结果(与我想要的结果相去甚远)。我正在使用地图有选择地从贷款对象中选择键。
const loanrequests = await db.collection('loanrequest').aggregate([
$match: requester: ObjectID(user.id) ,
$project:
lendingpartner:
name: 1,
branchname: '$branch.branchname',
,
loans:
$map:
input: '$loans',
as: 'loan',
in:
loanid: '$$loan.loanid',
loanamount: '$$loan.amount',
jewels: '$$loan.jewels',
,
,
,
,
,
/*
* I experimented with unwind here. Tried adding
* $unwind: '$loans' ,
* $unwind: '$loans.jewels'
* but it does not give me the result I need (as already said before)
*/
]).toArray();
我想,我需要在投影之前执行$lookup
,但由于文档的 2 级嵌套结构(首先是 loans
数组,然后是 @987654331 @)
我今天开始使用 mongodb 聚合器,在寻找答案时,我偶然发现了类似的 Question,但它似乎更复杂,因此我更难理解。
谢谢!
【问题讨论】:
【参考方案1】:如果您没有尝试使用聚合来实现其他目标,则可以在 mongoose 中使用 .populate。
LoanReqests
.find(
requester: user.id,
name: 1, branch: 1, loans: 1 // Projection
)
.populate('loans.jewels');
如果您必须使用聚合来执行示例中没有的操作,那么 $unwind 确实是您最好的选择,但是在 $lookup 之后进行 $grouping 以获得您想要的输出。如果这对您不起作用,您能否详细说明 $unwind 的问题是什么?我猜这与您的问题中未列出的字段有关。
https://mongoplayground.net/p/O5pxWNy99J4
db.loanRequests.aggregate([
$project:
name: 1,
loans: 1,
branch: "$branch.name"
,
$unwind: "$loans"
,
$lookup:
localField: "loans.jewels",
foreignField: "_id",
from: "jewels",
as: "loans.jewels"
,
$group:
_id: "$_id",
name:
$first: "$name"
,
branch:
$first: "$branch"
,
loans:
$push: "$loans"
])
【讨论】:
你是对的,$unwind
后跟 $group
会导致所需的输出。以前不知道$group
,我对如何“解散”(在运行$unwind 之后)一无所知。我尝试建立您的解决方案,并获得了预期的结果。【参考方案2】:
正如@GitGitBoom 在上一个答案中提到的,$unwind
后跟$group
应该是这种方法。
当然,在分组之前(我认为它是“展开”运行 unwind 的结果),我需要运行 $lookup
以填充 loans.jewels
这是基于上一个答案构建的整个解决方案。
const loanRequests = await db.collection('loanRequest').aggregate([
$match: requester: ObjectID(user.id) ,
$project:
lender: '$lendingpartner.name',
branch: '$lendingpartner.branch.branchname',
loans: 1,
,
,
$unwind: '$loans' ,
$lookup:
localField: 'loans.jewels',
from: 'pledgedJewel',
foreignField: '_id',
as: 'loans.jewels',
,
,
$group:
_id: '$_id',
branch: $first: '$branch' ,
lender: $first: '$lender' ,
loans: $push: '$loans' ,
,
,
$project:
_id: 1,
branch: 1,
lender: 1,
loans: 1,
,
,
]).toArray();
类型不匹配的问题
另一个问题是,我的$lookup
由于类型不匹配而无法正常工作。在我运行聚合的 loanRequest 集合中,loans.jewels
中的 id 是 string
类型,而承诺珠宝中的外部字段 _id
是 ObjectId
这可以通过使用$toObjectId
或$toString
来解决(仅在mongodb 版本>= 4.0 中支持)
$project: jewelObjId: $toObjectId: '$loans.jewels' , // for mongodb >= 4.0
$lookup:
localField: 'jewelObjId', // for mongodb >= 4.0
from: 'pledgedjewel',
foreignField: '_id',
as: 'loans.jewels',
,
,
但是,我在较低版本的 mongodb 上运行,因此这些聚合对我不起作用。解决这个问题的唯一方法是将loans.jewels
的类型更改为ObjectId
,而不是像我那样保持string
。
关于类型不匹配的更多信息
Need a workaround for lookup of a string to objectID foreignField
Mongodb Join on _id field from String to ObjectId
【讨论】:
以上是关于MongoDB $lookup 在 2 级嵌套文档上不使用 $unwind的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB 聚合 - 我如何“$lookup”嵌套文档“_id”?