Search code examples
arraysmongodbmongodb-update

MongoDB - Update multiple subdocuments in array in multiple documents


I'm making a node.js website. I have a posts collection in which comments for posts are stored in an array with the comment author's details as nested object.

This is new post's schema:

{
    "text": text,
    "image": image,
    "video": video,
    "type": type,
    "createdAt": createdAt,
    "reactions": [],
    "comments": [],
    "shares": [],
    "user": {
        "_id": user._id,
        "username": user.username
    }
}

This is new comment being pushed to its post:

$push: {
    "comments": {
        "_id": commentId,
        "user": {
            "_id": user._id,
            "type": type,
            "name": user.name,
            "profileImage": user.photo,
        },
        "comment": comment,
        "createdAt": createdAt,
        "replies": []
    }
}                           

To avoid storing comments in another collection and doing complex multiple lookups(I'm doing 1 lookup to get post author details but couldn't add another to make it work for comments) to consolidate the newsfeed I decided to save comments and their author's details embedded in the posts.

Now when user profile picture is updated all the comments have to be updated to show the new picture.

I included this updateMany query along with the photo updation route in server.js file:

database.collection("posts").updateMany({
    "comments.user._id": user._id,
    "comments.user.type": "friend"
}, {
    $set: {
        "comments.$.user.profileImage": photo
    }
});

The problem here is that this updates only the first matching comment in all posts.

I need to update all matching comments in all posts.

I'm actually just learning by doing this following youtube videos, so please help me.


Solution

  • You need to use arrayFilters I think.

    If I've understand well your question this example should be similar to your DB.

    The query is this:

    db.collection.update({
      "comments.user._id": 1,
      "comments.user.type": "friend"
    },
    {
      "$set": {
        "comments.$[element].user.profileImage": "new"
      }
    },
    {
      "arrayFilters": [
        {
          "$and": [
            {
              "element.user._id": 1
            },
            {
              "element.user.type": "friend"
            }
          ]
        }
      ],
      "multi": true
    })
    

    First part is the same, and almost the second. You have to add element position into the array that is defined in the next step.
    Using arrayFilters, you look for those whose match the comaprsion into $and. And only those ones will be updated.

    Note that using updateMany() method, is not neccesary using {multi: true}