【问题标题】:How to copy all fields from first document in an aggregate (re)grouping如何在聚合(重新)分组中从第一个文档中复制所有字段
【发布时间】:2020-07-07 14:37:04
【问题描述】:

在一个名为 test 的集合中,我有一些类似于以下内容的文档:

{
    "_id" : "PRQA2311",
    "name": "Binder",
    "category": "Office Supplies",
    "price": 3.99,
    "...": "...",
    "variants" : [
        {
            "barcode" : "2314399903212",
            "attributes" : {
                "color" : "red",
                "size" : "Letter"
            }
        },
        {
            "barcode" : "2314399903213",
            "attributes" : {
                "color" : "red",
                "size" : "A4"
            }
        },
        {
            "barcode" : "2314399903214",
            "attributes" : {
                "color" : "blue",
                "size" : "Letter"
            }
        },
        {
            "barcode" : "2314399903215",
            "attributes" : {
                "color" : "blue",
                "size" : "A4"
            }
        }
    ]
}

我使用以下聚合管道将它们拆分和重新组合,以便每个产品只有一种颜色:

db.test.aggregate([
    {
        $unwind: "$variants"
    },
    {
        $group: {
            _id: {
                $concat: [
                    { "$toString": "$_id" },
                    ".",
                    { "$toString": "$variants.attributes.color" }
                ]
            },
            name: { $first: "$name" },
            variants: { $push: "$variants" }
        }
    }
]).pretty();

这很好用,但我也想复制其他字段。虽然我可以像使用name 字段一样将字段显式添加到组聚合中来做到这一点。如果我可以复制所有字段,它将使开发和维护更容易。

查看this question 的公认答案似乎在过去可能是不可能的。然而,第二个答案暗示,即使提供的解决方案不能立即适用于我的问题,也可能使用较新版本的 MongoDB。

我认为$mergeObjects$replaceRoot 可能有解决方案,但我还没有解决。

【问题讨论】:

  • data:{$push:$$ROOT} 怎么样,而不是所有字段,即名称、变体。您将从之前的管道中获取所有字段吗?
  • @Gibbs 我实际上一直在玩类似"object": {"$first": "$$ROOT"} 的东西,但是我仍然需要弄清楚如何对其进行转换,以便字段最终处于正确的位置。我在考虑 $addFields 或 $mergeObjects 后跟 replaceRoot。但我还没有多少运气。

标签: mongodb copy aggregation-framework grouping


【解决方案1】:

您可以将整个“根”文档 ($$ROOT) 放在一个字段中,然后使用 $mergeObjects 将其与 variants 字段合并

db.test.aggregate([
  {
    $unwind: "$variants"
  },
  {
    $group: {
      _id: {
        $concat: [
          { "$toString": "$_id" },
          ".",
          { "$toString": "$variants.attributes.color" }
        ]
      },
      variants: {
        $push: "$variants"
      },
      _root: {
        $first: "$$ROOT" // put all fields inside _root
      }
    }
  },
  {
    $replaceRoot: { // replace the root document with the combination of _root and variants, and _id if you want to keep the concatenated _id
      newRoot: {
        $mergeObjects: [
          "$_root",
          { _id: "$_id", variants: "$variants" }
        ]
      }
    }
  }
])

Mongo Playground

提示:对于 MongoDB v4.2 或更高版本,您可以使用 { $replaceWith: <doc> } 而不是 { $replaceRoot: { newRoot: <doc> } }

【讨论】:

    【解决方案2】:
    更新

    我已验证这也有效,目前是我的首选方法,等待基准测试:

    db.test.aggregate([
        {
            $unwind: "$variants"
        },
        {
            $group: {
                _id: {
                    $concat: [
                        { "$toString": "$_id" },
                        ".",
                        { "$toString": "$variants.attributes.color" }
                    ]
                },
                object: { $first: "$$ROOT" },
                variants: { $push: "$variants" }
            }
        },
        {
            $set: {
                "object._id": "$_id",
                "object.variants": "$variants",
            }
        },
        {
            $replaceRoot: { newRoot: "$object" }
        }
    ]).pretty();
    

    我想通了,虽然我更喜欢thammada.ts' answer

    db.test.aggregate([
        {
            $unwind: "$variants"
        },
        {
            $group: {
                _id: {
                    $concat: [
                        { "$toString": "$_id" },
                        ".",
                        { "$toString": "$variants.attributes.color" }
                    ]
                },
                object: { $first: "$$ROOT" },
                variants: { $push: "$variants" }
            }
        },
        {
            $unset: ["object._id", "object.variants"]
        },
        {
            $set: {
                "object._id": "$_id",
                "object.variants": "$variants",
            }
        },
        {
            $replaceRoot: { newRoot: "$object" }
        }
    ]).pretty();
    

    【讨论】:

    • 如果您要覆盖这些字段,实际上不需要$unset
    • @thammada.ts 哇,太棒了。实际上,我认为我最终可能会在没有未设置阶段的情况下使用我的答案,因为它对眼睛更容易。虽然我对性能很好奇。我必须运行一些基准测试
    猜你喜欢
    • 2020-03-06
    • 1970-01-01
    • 2017-11-27
    • 2015-01-10
    • 2021-04-13
    • 2023-03-05
    • 2021-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多