Home > Software engineering >  MongoDB update - How to change/rename a field name in a Nested Array?
MongoDB update - How to change/rename a field name in a Nested Array?

Time:11-30

MongoDB Documents:

[
  {
    "_id": 0,
    "KeyResults": [
      {
        "Title": "Test-1",
        "KeyResultWatchers": [
          {
            "DisplayName": "Billy CS"
          },
          {
            "DisplayName": "Ayşe CS"
          }
        ]
      }
    ]
  },
  {
    "_id": 1,
    "KeyResults": [
      {
        "Title": "Test-2",
        "KeyResultWatchers": null
      }
    ]
  },
  {
    "_id": 2,
    "KeyResults": [
      {
        "Title": "Test-3",
        "KeyResultWatchers": []
      }
    ]
  },
  {
    "_id": 3,
    "KeyResults": [
      {
        "Title": "Test-4",
        "KeyResultWatchers": [
          {
            "DisplayName": "Billy CS"
          },
          {
            "abd": "idk"
          }
        ]
      }
    ]
  },
  {
    "_id": 4,
    "KeyResults": [
      {
        "Title": "Test-5-0",
        "KeyResultWatchers": [
          {
            "DisplayName": "Billy CS"
          },
          {
            "DisplayName": "Ayşe CS"
          }
        ]
      },
      {
        "Title": "Test-5-1",
        "KeyResultWatchers": [
          {
            "DisplayName": "Billy CS-1"
          },
          {
            "DisplayName": "Ayşe CS-1"
          }
        ]
      }
    ]
  }
]

I want to replace property name DisplayName with FullName.

MongoDb version: 5.0.6

Query:

db.Objective.updateMany({}, {$rename: {'KeyResults.KeyResultWatchers.DisplayName':'KeyResults.KeyResultWatcher.FullName'}})

Ran into an Error: cannot use the part (KeyResults of KeyResults.KeyResultWatchers.DisplayName) to traverse the element.

CodePudding user response:

There is no simple way to rename the field which is in a nested array using $rename!

  1. If your collection is way too big (Millions of docs/ GB's of data size), then it may be ideal to update the field via a script. (First add a new field & remove the existing field later).

or

  1. Since you're on MongoDB > 4.2, you can take advantage of using aggregation pipeline in update operations:

     const aggregationPipeline = [
     {
       "$set": {
         "KeyResults": { // re-create 'KeyResults' array
           $map: {
             input: "$KeyResults", // iterate over 'KeyResults' array
             in: {
               $mergeObjects: [ // merge all fields inside 'KeyResults' each object with it's newly created 'KeyResultWatchers' array
                 "$$this", // each object inside 'KeyResults' array
                 {
                   KeyResultWatchers: { // re-create 'KeyResultWatchers' arraay
                     $map: {
                       input: "$$this.KeyResultWatchers", // iterate over 'KeyResultWatchers' array
                       as: "eachWatcher", //each object inside 'KeyResultWatchers' array
                       in: {
                         $cond: [ { $not: [ "$$eachWatcher.DisplayName" ] }, "$$eachWatcher", { FullName: "$$eachWatcher.DisplayName" } ] // If you've more fields along with 'FullName' add those in else block of $cond
                       }
                     }
                   }
                 }
               ]
             }
           }
         }
       }
     }
    ]
    
    db.Objective.update({"KeyResults.KeyResultWatchers.DisplayName": {$exists: true }}, aggregationPipeline ,{multi: true}) //Using filter part of query we are excluding doc`s where `KeyResults.KeyResultWatchers: null/[]`
    

Note:

  1. I will always suggest to run this aggregation pipeline first to check the performance impact & also make sure to have proper indexes on your DB.

  2. You might not need all of the logic given here, this might cover more scenario's in general, which might not exist for you! Please go through the query & ignore those if not needed.

Test aggregation pipeline: mongoDB-playground

  • Related