mongodbaggregation-frameworkmongodb-update

Replace substring in array of objects with nested objects


I am struggling with avatar URL update in nested objects array persisted in MongoDB.

Avatar URL needs to be updated from "https://old.com/image.jpg" to "https://new.com/image.jpg"

The document structure is at the end of this post. I need to update the avatar in three places:

  1. organizer.avatar - it works (see below or visit Mongo playground-organizer avatar)
  2. meetings[].owner.avatar - similar issue found on StackOverflow, but it does not work because of different document structure (see below or visit Mongo playground-owner avatar)
  3. meetings[].participants[].avatar - I have not idea how to handle this.

Update organizer.avatar

It is pretty straightforward as you can see in Mongo playground- organizer avatar.

db.collection.update({},
[
  {
    "$set": {
      "organizer.avatar": {
        $replaceOne: {
          input: "$organizer.avatar",
          find: "https://old.com/",
          replacement: "https://new.com/"
        }
      }
    }
  }
])

Update meetings[].owner.avatar

It is similar to another issue reported on StackOverflow, but here avatar is nested inside the owner object. In this case, operator $mergeObjects does not work as I would expect, because it does not merge objects, but overrides it - see Mongo playground-owner avatar. How to fix this?

db.collection.update({},
[
  {
    "$set": {
      "meetings": {
        $map: {
          input: "$meetings",
          in: {
            $mergeObjects: [
              {
                owner: "$$this.owner"
              },
              {
                owner: {
                  avatar: {
                    $replaceOne: {
                      input: "$$this.owner.avatar",
                      find: "https://old.com/",
                      replacement: "https://new.com/"
                    }
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
])

Update meetings[].participants[].avatar

When it comes to updating meetings[].participants[].avatar - do you have any idea how to handle this?

Example document:

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "eventName": "Welcome Event",
    "meetings": [
      {
        "owner": {
          "avatar": "https://old.com/dwight-schrute.png",
          "name": "Dwight Schrute"
        },
        "participants": [
          {
            "avatar": "https://old.com/kevin-malonoe.png",
            "name": "Kevin Malonoe"
          },
          {
            "avatar": "https://old.com/creed-bratton.png",
            "name": "Creed Bratton"
          }
        ]
      },
      {
        "owner": {
          "avatar": "https://old.com/jim-halpert.png",
          "name": "Jim Halpert"
        },
        "participants": [
          {
            "avatar": "https://old.com/pam-beesly.png",
            "name": "Pam Beesly"
          }
        ]
      }
    ],
    "organizer": {
      "avatar": "https://old.com/michale-scott.png",
      "name": "Michael Scott"
    }
  }
]

Solution

  • Your update query should consist of these concepts to achieve the expected result:

    1. Merge iterated object.

    2. Update the iterated owner's avatar.

    3. Update the element's avatar in the iterated object's participants array.

    db.collection.update({},
    [
      {
        "$set": {
          "meetings": {
            $map: {
              input: "$meetings",
              in: {
                $mergeObjects: [
                  "$$this",
                  {
                    owner: {
                      $mergeObjects: [
                        "$$this.owner",
                        {
                          avatar: {
                            $replaceOne: {
                              input: "$$this.owner.avatar",
                              find: "https://old.com/",
                              replacement: "https://new.com/"
                            }
                          }
                        }
                      ]
                    }
                  },
                  {
                    participants: {
                      $map: {
                        input: "$$this.participants",
                        as: "p",
                        in: {
                          $mergeObjects: [
                            "$$p",
                            {
                              avatar: {
                                $replaceOne: {
                                  input: "$$p.avatar",
                                  find: "https://old.com/",
                                  replacement: "https://new.com/"
                                }
                              }
                            }
                          ]
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      }
    ])
    

    Demo @ Mongo Playground