繁体   English   中英

MongoDB $lookup 在 2 级嵌套文档上,而不使用 $unwind

[英]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 中的外部字段_idObjectId

这可以通过使用$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

更多关于类型不匹配

需要一种解决方法来查找 objectID foreignField 的字符串

Mongodb 加入 _id 字段从 String 到 ObjectId

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM