简体   繁体   中英

Merge results in mongo query with conditions

Use case:

I have n number of jobs, I want to merge the data for those jobs such that if value to corresponding subkey is passed in 1 case, it should mark it as passed.

eg Job1 Detailed Object:

{"Name" : [{"No." : "119","Time" : "t"}],
"Results":[{**"K1"** : {"Counters" : x, "TCR" : [{"Name" : "K11", "Result" : "PASSED"}, 
                                             {"Name" : "K12","Result" : **"FAILED"**},
                                             {"Name" : "K13","Result" : **"PASSED"**}]
                    },
            "K2" : {"Counters": y, "TCR" : [{"Name" : "K21","Result" : "PASSED"},              
                                            {"Name" : "K22","Result" : "PASSED"}]
                      
                    }
            
           ]
}

Job2 Detailed Object:

{"Name" : [{"No." : "120","Time" : "t1"}],
"Results":[{"**K1"** : {"Counters" : x, "TCR" : [{"Name" : "K11", "Result" : "PASSED"}, 
                                             {"Name" : "K12","Result" : **"PASSED"**},
                                             {"Name" : "K13","Result" : **"FAILED"**}]
                    },
            "K3" : {"Counters": y, "TCR" : [{"Name" : "K31","Result" : "PASSED"},              
                                            {"Name" : "K32","Result" : "PASSED"}]
                      
                    }
            
           ]
}

Expected Output

{"Name" : [{"No." : "119-120","Time" : "lowest(t,t1)"}],
"Results":[{**"K1"** : {"Counters" : x, "TCR" : [{"Name" : "K11", "Result" : "PASSED"}, 
                                             {"Name" : "K12","Result" : **"PASSED"**},
                                             {"Name" : "K13","Result" : **"PASSED"**}]
                    },
            "K2" : {"Counters": y, "TCR" : [{"Name" : "K21","Result" : "PASSED"},              
                                            {"Name" : "K22","Result" : "PASSED"}]
                      
                    },
             "K3" : {"Counters": y, "TCR" : [{"Name" : "K31","Result" : "PASSED"},              
                                            {"Name" : "K32","Result" : "PASSED"}]
                      
                    }
            
           ]
}

Explanation:

Key k1 is common in both dicts, so inside k1, we have key k12 and k13, which are passed in one but failed in other. So, in expected output, I need key k1 with keys k12 and k13 having value as passed, as they are passed in one case. Along with that, K2 and K3 will come as it is.

I reached till point where I aggregated same key data in one row, but how to proceed for further comparisons.

query using -

aggregate([{$match: {
  $or:[{"Name.No":"119"},{"Name.No":"120"}]
}}, {$project: {
  x:{$objectToArray:"$Results"}
}},{$unwind: "$x"},{$group: {_id: "$x.k", distinctVals: {$addToSet: "$x.v.TCR"}}}])

The name/result fields are:
in an object that is
in an array that is
embedded in an object that is
embedded in another object that is
in an array that is
embedded in the document.

Thats a lot of layers to peel.

So to get to the name/result pairs:

  • unwind array in the document
  • convert the embedded objects to arrays
  • unwind the converted arrays
  • reach into the last document and unwind the deep array

Then to combine them

  • group by the name part, push all the pass/fail results into an array
  • reduce over the constructed array to determine the overall pass/fail value
  • group by the key (K1, K2, etc) and push the name/result into an array
  • group by null (ie no group making a single document) push they keys into a results array
  • reconstruct the document structure

Through all of those steps, maintain the global fields like Name and Time.

That might look like:

db.collection.aggregate([
  {$match: {"Name.No": {$in:["119","120"]}}},
  {$unwind: "$Results"},
  {$set: {
      Results: {$objectToArray: "$Results"}
  }},
  {$unwind: "$Results"},
  {$unwind: "$Results.v.TCR"},
  {$group: {
      _id: {
        resultKey: "$Results.k",
        TCRName: "$Results.v.TCR.Name"
      },
      Result: {$push: "$Results.v.TCR.Result"},
      Counters: {$first: "$Results.v.Counters"},
      No: {$addToSet: "$Name.No"},
      Time: { $min: "$Name.Time"}
  }},
  {$set: {
      No: {
        $reduce: {
          input: "$No",
          initialValue: [],
          in: {$setUnion: ["$$value","$$this"]}
        }
      },
      Result: {
        $cond: {
          if: {$in: ["PASSED","$Result"]},
          then: "PASSED",
          else: "FAILED"
        }
      }
  }},
  {
    $group: {
      _id: "$_id.resultKey",
      No: {$addToSet: "$No"},
      Time: {$min: "$Time"},
      Counters: {$first: "$Counters"},
      TCR: {
        $push: {
          $arrayToObject: [
            [
              {"k": "name","v": "$_id.TCRName"},
              {"k": "Result",v: "$Result"}
            ]
          ]
        },
        
      }
  }},
  {$set: {
      No: {
        $reduce: {
          input: "$No",
          initialValue: [],
          in: {$setUnion: ["$$value","$$this"]}
        }
      }
  }},
  {$group: {
      _id: null,
      No: {$push: "$No"},
      Time: {$min: "$Time"},
      Results: {
        $push: {"k": "$_id",
                "v": {
                      Counters: "$Counters",
                      TCR: "$TCR"
                     }
               }
      }
  }},
  {$project: {
      Name: {
        No: {
          $reduce: {
            input: "$No",
            initialValue: [],
            in: {$setUnion: ["$$value","$$this"]}
          }
        },
        Time: {$min: "$Time"}
      },
      Results: {$arrayToObject: "$Results"}
  }}
])

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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