如何将不同数组中的两个匹配对象合并为一个对象?

Posted

技术标签:

【中文标题】如何将不同数组中的两个匹配对象合并为一个对象?【英文标题】: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 并尝试使用聚合来实现这一点。

提前致谢。

【问题讨论】:

【参考方案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"
             
  
])

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

【讨论】:

谢谢。它拯救了我的一天。

以上是关于如何将不同数组中的两个匹配对象合并为一个对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何将数组的两个对象合并为一个?

将可比较的值类型 Int 应用于 Swift 4 中的 UIImage 对象

如何映射两个数组并将匹配作为按钮返回?

根据匹配的 ID 合并两个数组中的项目

将两个数组(键和值)合并为一个对象[重复]

如何比较2个长度数组的对象条目的属性并合并为一个? [复制]