【问题标题】:How to merge two matching objects from different array into one object?如何将不同数组中的两个匹配对象合并为一个对象?
【发布时间】:2018-06-08 00:09:40
【问题描述】:

我有一种情况,我从聚合中得到一个结果,我以这种格式获取数据。

{
    "_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
    "bacId" : "BAC0023444",
    "cardId" : "2",
    "defaultCardOrder" : "2",
    "alias" : "Finance",
    "label" : "Finance",
    "for" : "",
    "cardTooltip" : {
        "enable" : true,
        "text" : ""
    },
    "dataBlocks" : [
        {
            "defaultBlockOrder" : "1",
            "blockId" : "1",
            "data" : "0"
        },
        {
            "defaultBlockOrder" : "2",
            "blockId" : "2",
            "data" : "0"
        },
        {
            "defaultBlockOrder" : "3",
            "blockId" : "3",
            "data" : "0"
        }
    ],
    "templateBlocks" : [
        {
            "blockId" : "1",
            "label" : "Gross Profit",
            "quarter" : "",
            "data" : "",
            "dataType" : {
                "typeId" : "2"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "blockId" : "2",
            "label" : "Profit Forecast",
            "quarter" : "",
            "data" : "",
            "dataType" : {
                "typeId" : "2"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "blockId" : "3",
            "label" : "Resource Billing",
            "quarter" : "",
            "data" : "",
            "dataType" : {
                "typeId" : "2"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        }
    ]
},
{
    "_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
    "bacId" : "BAC0023444",
    "cardId" : "3",
    "defaultCardOrder" : "3",
    "alias" : "Staffing",
    "label" : "Staffing",
    "for" : "",
    "cardTooltip" : {
        "enable" : true,
        "text" : ""
    },
    "dataBlocks" : [
        {
            "defaultBlockOrder" : "1",
            "blockId" : "1",
            "data" : "1212"
        },
        {
            "defaultBlockOrder" : "2",
            "blockId" : "2",
            "data" : "1120"
        },
        {
            "defaultBlockOrder" : "3",
            "blockId" : "3",
            "data" : "1200"
        }
    ],
    "templateBlocks" : [
        {
            "blockId" : "1",
            "label" : "Staffing Planner",
            "quarter" : "",
            "data" : "",
            "dataType" : {
                "typeId" : "1"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "blockId" : "2",
            "label" : "Baseline",
            "quarter" : "",
            "data" : "",
            "dataType" : {
                "typeId" : "1"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "blockId" : "3",
            "label" : "Projected",
            "quarter" : "",
            "data" : "",
            "dataType" : {
                "typeId" : "1"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        }
    ]
}

现在我想比较每一行的两个对象数组,在这种情况下,它的“dataBlocks”和“templateBlocks”基于“blockId”,我想得到以下格式的结果。

{
    "_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
    "bacId" : "BAC0023444",
    "cardId" : "2",
    "defaultCardOrder" : "2",
    "alias" : "Finance",
    "label" : "Finance",
    "for" : "",
    "cardTooltip" : {
        "enable" : true,
        "text" : ""
    },
    "blocks" : [
        {
            "defaultBlockOrder" : "1",
            "blockId" : "1",
            "data" : "0",
            "label" : "Gross Profit",
            "quarter" : "",
            "dataType" : {
                "typeId" : "2"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "defaultBlockOrder" : "2",
            "blockId" : "2",
            "data" : "0",
            "label" : "Profit Forecast",
            "quarter" : "",
            "dataType" : {
                "typeId" : "2"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "defaultBlockOrder" : "3",
            "blockId" : "3",
            "data" : "0",
            "label" : "Resource Billing",
            "quarter" : "",
            "dataType" : {
                "typeId" : "2"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        }
    ]
},
{
    "_id" : ObjectId("5a42432d69cbfed9a410e8ad"),
    "bacId" : "BAC0023444",
    "cardId" : "3",
    "defaultCardOrder" : "3",
    "alias" : "Staffing",
    "label" : "Staffing",
    "for" : "",
    "cardTooltip" : {
        "enable" : true,
        "text" : ""
    },
    "dataBlocks" : [
        {
            "defaultBlockOrder" : "1",
            "blockId" : "1",
            "data" : "1212",
            "label" : "Staffing Planner",
            "quarter" : "",
            "dataType" : {
                "typeId" : "1"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "defaultBlockOrder" : "2",
            "blockId" : "2",
            "data" : "1120",
            "label" : "Baseline",
            "quarter" : "",
            "dataType" : {
                "typeId" : "1"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        },
        {
            "defaultBlockOrder" : "3",
            "blockId" : "3",
            "data" : "1200",
            "label" : "Projected",
            "quarter" : "",
            "dataType" : {
                "typeId" : "1"
            },
            "tooltip" : {
                "enable" : true,
                "text" : ""
            }
        }
    ]
}

是否可以使用 mongodb 完成它?我正在使用 3.4 并尝试使用聚合来实现这一点。

提前致谢。

【问题讨论】:

    标签: mongodb aggregation-framework spring-mongo spring-mongodb


    【解决方案1】:

    您可以在 3.6 中尝试以下聚合。

    下面的查询迭代 dataBlocks 数组并将数据块元素与模板块元素合并。使用$indexofArray 查找模板块,它定位具有匹配块id 的数组索引和$arrayElemAt 以访问找到的索引处的元素。

    db.collection_name.aggregate([{"$addFields":{
      "blocks":{
        "$map":{
          "input":"$dataBlocks",
          "in":{
            "$mergeObjects":[
              "$$this",
              {"$arrayElemAt":[
                "$templateBlocks",
                {"$indexOfArray":["$templateBlocks.blockId","$$this.blockId"]}
                ]
              }
            ]
          }
        }
      }
    }}])
    

    对于 3.4,将 $mergeObjects 替换为 $arrayToObject$objectToArray$concatArrays 的组合以合并两个数组中的每个数组元素。

    db.collection_name.aggregate([{"$addFields":{
      "blocks":{
        "$map":{
          "input":"$dataBlocks",
          "in":{
            "$arrayToObject":{
              "$concatArrays":[
                {"$objectToArray":"$$this"},
                {"$objectToArray":{
                  "$arrayElemAt":[
                    "$templateBlocks",
                    {"$indexOfArray":["$templateBlocks.blockId","$$this.blockId"]
                    }
                  ]
                }}
              ]
            }
          }
        }
      }
    }}])
    

    您可以使用排除项目作为最后阶段从输出中删除数组字段。

    {"$project":{"templateBlocks":0,"dataBlocks":0}}
    

    【讨论】:

    • { "$indexOfArray":["$templateBlocks","$$this.blockId"] } 的部分不起作用,因为templateBlocks 包含对象而不是元素,因此它们与给定的blockId 不直接匹配,它们需要以某种方式映射,以便您可以引用每个templateBlockblockId 属性。
    • 我建议对@Argento 提到的问题进行编辑。否则,这绝对应该是公认的答案。
    • @Argento,@Burawi,我刚刚添加了{ "$indexOfArray":["$templateBlocks .blockId ","$$this.blockId"] },它似乎有效。
    【解决方案2】:

    以下查询完成了这项工作:

    db.merge.aggregate([
      // unwind twice
      {$unwind: "$templateBlocks"},
      {$unwind: "$dataBlocks"},
      // get rid of documents where dataBlocks.blockId and 
      // templateBlocks.blockId are not equal
      {$redact: {$cond: [{
                            $eq: [
                                   "$dataBlocks.blockId",
                                   "$templateBlocks.blockId"
                                 ]
                          },
                          "$$KEEP",
                          "$$PRUNE"
                        ]
                }
      },
      // merge dataBlocks and templateBlocks into a single document
      {$project: {
                    bacId: 1,
                    cardId: 1,
                    defaultCardOrder: 1,
                    alias: 1,
                    label: 1,
                    for: 1,
                    cardTooltip: 1,
                    dataBlocks: {
                                  defaultBlockOrder: "$dataBlocks.defaultBlockOrder",
                                  blockId: "$dataBlocks.blockId",
                                  data: "$dataBlocks.data",
                                  label: "$templateBlocks.label",
                                  quarter: "$templateBlocks.quarter",
                                  data: "$templateBlocks.data",
                                  dataType: "$templateBlocks.dataType",
                                  tooltip: "$templateBlocks.tooltip"
                                }
                 }
          },
          // group to put correspondent dataBlocks to an array
          {$group: {
                  _id: {
                         _id: "$_id",
                         bacId: "$bacId",
                         cardId: "$cardId",
                         defaultCardOrder: "$defaultCardOrder",
                         alias: "$alias",
                         label: "$label",
                         for: "$for",
                         cardTooltip: "$cardTooltip"
                       },
                  dataBlocks: {$push: "$dataBlocks" }
               }
      },
      // remove the unnecessary _id object
      {$project: {
                   _id: "$_id._id",
                   bacId: "$_id.bacId",
                   cardId: "$_id.cardId",
                   defaultCardOrder: "$_id.defaultCardOrder",
                   alias: "$_id.alias",
                   label: "$_id.label",
                   for: "$_id.for",
                   cardTooltip: "$_id.cardTooltip",
                   dataBlocks: "$dataBlocks"
                 }
      }
    ])
    

    考虑到性能取决于数据集的大小,因为查询展开两次,它可能会产生大量的中间文档。

    【讨论】:

    • 谢谢。它拯救了我的一天。
    猜你喜欢
    • 2023-01-14
    • 2021-01-18
    • 2020-03-29
    • 2021-07-27
    • 2021-10-29
    • 2018-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多