Home > Software design >  MongoDB 5.0 how to upsert array data?
MongoDB 5.0 how to upsert array data?

Time:10-28

I have a mongo document like this.

{
    "_id": "a74e283a",
    "data": [
        {
            "origin": "apple",
            "edit": "fruit"
        },
        {
            "origin": "grape",
            "edit": "fruit"
        },
        {
            "origin": "melon",
            "edit": "fruit"
        }
    ]
}

I want to be able to insert and update the array with a single mongo command and not use a conditional within a find() then run insert() and update() depending on the presence of the object.

example if I update the array with this:

{
    "data": [
        {
            "origin": "apple",
            "edit": "fruit_edit"
        },
        {
            "origin": "pear",
            "edit": "fruit"
        }
    ]
}

Result must be like this:

{
    "_id": "a74e283a",
    "data": [
        {
            "origin": "apple",
            "edit": "fruit_edit"
        },
        {
            "origin": "grape",
            "edit": "fruit"
        },
        {
            "origin": "melon",
            "edit": "fruit"
        },
        {
            "origin": "pear",
            "edit": "friut"
        }
    ]
}

CodePudding user response:

You can update with an aggregation pipeline. Put a $set in the aggregation pipeline and use $reduce to process the upsert logic.

In the $reduce, put your edit array as the initial value. When there is a match, just keep the accumulator value as the edit is already in the accumulator. When there is unmatched, append the array entry to the accumulator.

db.collection.update({},
[
  {
    $set: {
      data: {
        "$reduce": {
          "input": "$data",
          "initialValue": // assign your edit data as init value
          [
            {
              "origin": "apple",
              "edit": "fruit_edit"
            },
            {
              "origin": "pear",
              "edit": "fruit"
            }
          ],
          "in": {
            "$cond": {
              "if": {
                $in: [
                  "$$this.origin",
                  "$$value.origin"
                ]
              },
              "then": "$$value",
              "else": {
                "$concatArrays": [
                  "$$value",
                  [
                    "$$this"
                  ]
                ]
              }
            }
          }
        }
      }
    }
  }
])

Here is the Mongo playground for your reference.

CodePudding user response:

Query

  • put you new data on "ndata"
  • one more variable is defined "ndata_origin" to do the project 1 time only
  • this filters the old data, and remove all the new-data => old-without-new
  • concat old-without-new newdata

Test code here

update({},
[{"$set": 
   {"data": 
     {"$let": 
       {"vars": 
         {"ndata": 
          [{"origin": "apple", "edit": "fruit_edit"},
           {"origin": "pear", "edit": "fruit"}]},
        "in": 
         {"$let": 
           {"vars": {"ndata_origin": "$$ndata.origin"},
            "in": 
            {"$concatArrays": 
              [{"$filter": 
                 {"input": "$data",
                  "cond": 
                  {"$not": [{"$in": ["$$this.origin", "$$ndata_origin"]}]}}},
               "$$ndata"]}}}}}}}])
  • Related