【问题标题】:How to add entity field to joined documents?如何将实体字段添加到加入的文档?
【发布时间】:2022-11-11 03:14:02
【问题描述】:

我有一个电子商务服务器,其中有一个products 和一个orders 集合。

任何product 文档都包含一个唯一的productId,例如prod_123。 每个order 文档都包含一个lineItems(数组)字段,该字段返回所购买产品的productIds 以及相应的quantity 购买例如

[{ productId: 'prod_123', quantity: 2 }, { productId: 'prod_234', quantity: 7 }, ...]

当我的客户获取他们的订单时,我想用products 集合中的匹配产品文档填充每个lineItems 元素的productId

我已经编写了一个 mongoDB 聚合管道来实现这一点,到目前为止就是这样:

 const orderPipeline = [
    {
      $match: { customerId: 'the customer's ID' },
    },
    {
      $lookup: {
        from: 'products',
        let: { productIds: '$lineItems.productId' },
        pipeline: [
          { $match: { $expr: { $in: ['$productId', '$$productIds'] } } },
          //*** somehow, need to add in corresponding `lineItem.quantity` here
        ],
        as: 'products',
      },
    },
    { $unset: ['lineItems'] },
  ];

但是,如您所见,虽然连接正在进行,但在删除 lineItems 之前,我无法弄清楚如何将匹配产品的 quantity 添加到连接的 product 中。

如何将对应的quantity 添加到对应的匹配product 中?

【问题讨论】:

  • products 中的文档是否基于 productId 是唯一的? lineItems 中是否会出现在products 中没有相应条目的产品?换句话说,这将是数组中的条目和另一个集合中的文档之间的1:1 匹配吗?
  • 是的,products 中的文档基于其productId 是唯一的。不,每个lineItem 都会有一个匹配的product

标签: mongodb mongoose aggregation-framework


【解决方案1】:

一种方法,我是很确定考虑到the comments 中提到的其他约束,将可以使用the $zip operator。总的来说,它会像这样工作:

  1. 使用从其他集合中检索到的信息执行$lookup 生成数组(products)。
  2. 使用$addFields 阶段作为大多数组合逻辑发生的地方。它会将$zip 两个数组放在一起,然后将$map 覆盖到$mergeObjects 每一对数组中成为一个对象。
  3. $unset 阶段结束以删除原始lineItems 字段(已合并到重新创建的products 数组中。

    完整的管道看起来像这样:

    db.orders.aggregate([
      {
        $match: {
          customerId: 123
        },
        
      },
      {
        $lookup: {
          from: "products",
          let: {
            productIds: "$lineItems.productId"
          },
          pipeline: [
            {
              $match: {
                $expr: {
                  $in: [
                    "$productId",
                    "$$productIds"
                  ]
                }
              }
            }
          ],
          as: "products",
          
        }
      },
      {
        "$addFields": {
          "products": {
            "$map": {
              "input": {
                "$zip": {
                  "inputs": [
                    "$lineItems",
                    "$products"
                  ]
                }
              },
              "in": {
                "$mergeObjects": "$$this"
              }
            }
          }
        }
      },
      {
        $unset: "lineItems"
      }
    ])
    

    Playground example here

    $map$mergeObjects: "$$this" 乍一看可能看起来很奇怪。这是必需的,因为$zip 将生成一个数组数组(每个数组有 2 个条目),例如:

        "zipped": [
          [
            {
              "productId": "a",
              "quantity": 1
            },
            {
              "_id": ObjectId("5a934e000102030405000002"),
              "productId": "a"
            }
          ],
          [
            {
              "productId": "b",
              "quantity": 2
            },
            {
              "_id": ObjectId("5a934e000102030405000003"),
              "productId": "b"
            }
          ],
          [
            {
              "productId": "c",
              "quantity": 3
            },
            {
              "_id": ObjectId("5a934e000102030405000004"),
              "productId": "c"
            }
          ]
        ]
    

    Here is a playground link 显示压缩后但在进一步处理之前的输出。)

    因此,我们需要将它们中的每一个折叠成一个对象,因此$mergeObjects。事实上,外部数组中的每个对象都是一个数组(与我们要合并的两个对象),这就是为什么我们可以简单地使用 "$$this" 作为运算符的输入表达式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-21
    • 1970-01-01
    • 1970-01-01
    • 2016-10-16
    • 1970-01-01
    • 2019-01-04
    • 2018-12-22
    • 2020-04-25
    相关资源
    最近更新 更多