[英]MongoDB $lookup on 2 level nested document without using $unwind
我有以下文件
贷款请求(只写我想要投射的钥匙)
{
"_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
(双 map?),但无法提出可行的解决方案。 无论如何,这似乎不是正确的方法。
这是我能想到的最好的(远非我想要的结果)。 我正在使用 map 从贷款 object 中选择性地选择密钥。
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
数组,然后loans.jewels
),我很难编写一个可行的解决方案
我今天开始使用 mongodb 聚合器,在寻找答案时,我偶然发现了一个类似的问题,但它似乎更复杂,因此我更难理解。
谢谢!
如果您没有尝试使用聚合来实现其他目标,则可以在 mongoose 中使用.populate。
LoanReqests
.find(
{requester: user.id},
{name: 1, branch: 1, loans: 1} // Projection
)
.populate('loans.jewels');
如果您必须使用聚合来执行示例中没有的操作,那么 $unwind 确实是您最好的选择,但是在 $lookup 之后进行 $grouping 以获得您想要的 output。 如果这对您不起作用,您能否详细说明 $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"
}
}
}
])
正如@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
类型,而 promisedJewel 中的外部字段_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
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.