Search code examples
javascriptnode.jsmongodbmongoosesubdocument

Mongoose function findByIdAndUpdate works as PUT not PATCH


I am new to mongoose and I have a problem when I try to partially update my subdocument. Although I use the $set operator, it resets the other fields even though they are not specified. I have noticed that sometimes it seems to work and sometimes it doesn't. I haven't been able to find a pattern. I need my update to be short.

This is my my schema.

 import { Schema, model } from "mongoose";

const Path = Schema({
    original: { type: String, required: true },
    tiny: { type: String },
    small: { type: String },
    medium: { type: String },
    large: { type: String },
    extra_large: { type: String },
});

const BusinessPictureSchema = Schema({
    Path: {
        type: Path,
        required: true
    },
    is_valid: {
        type: Boolean,
        required: true,
        default: false
    },
    date_validation: {
        type: Date
    }
}, {
    timestamps: true
});


module.exports = model('BusinessPicture', BusinessPictureSchema)

My test function:

async BusinessPicture() {
        const input_create = {
            Path: {
                original: "original file",
                tiny: "arhivo tiny"
            }
        }

        const newBusinessPicture = new BusinessPicture(input_create);
        await newBusinessPicture.save();

        const input_update = {
            Path: {
                original: "new original file!!!"
            }
        }

        await BusinessPicture.findByIdAndUpdate(newBusinessPicture.id, { $set: input_update });
        return BusinessPicture;


        // What i get
        // {
        //     "Path": {
        //         "original": "original file",
        //             "_id": {
        //             "$oid": "6347a31784167896b62648e5"
        //         }
        //     },
        //     "is_valid": false,
        //         "createdAt": {
        //         "$date": {
        //             "$numberLong": "1665639182788"
        //         }
        //     },
        //     "updatedAt": {
        //         "$date": {
        //             "$numberLong": "1665639191235"
        //         }
        //     },
        //     "__v": 0
        // }

        // What i hope to find
        // {
        //     "Path": {
        //       "original": "new original file!!",
        //       "tiny": "arhivo tiny",          <----THISSSS
        //       "_id": {
        //         "$oid": "6347a4201829a87212b7c120"
        //       }
        //     },
        //     "is_valid": false,
        //     "createdAt": {
        //       "$date": {
        //         "$numberLong": "1665639452549"
        //       }
        //     },
        //     "updatedAt": {
        //       "$date": {
        //         "$numberLong": "1665639456805"
        //       }
        //     },
        //     "__v": 0
        //   }
    }

Solution

  • Basically what mongoose is doing under the hood is:

    1. search for operators (like $set).
    2. inside value passed to operator search for other operators. When nothing is found mongoose will interpret the key as a document key and it's value as the document value of this key.

    When you pass

    {
      Path: {
        original: "new original file!!!"
      }
    }
    

    to $set operator, it will interpret this as: Set the document value of key Path to a this new object.

    What you should do to achieve only adjusting the original attribute:

    await BusinessPicture.findByIdAndUpdate(newBusinessPicture.id, { 
      $set: { 
        'Path.original': "new original file!!!"
      } 
    });