Home > database >  Prevent mongoose "Model.updateOne" from updating ObjectId(_id) of the model when using &qu
Prevent mongoose "Model.updateOne" from updating ObjectId(_id) of the model when using &qu

Time:11-27

I'm updating the age and name of a character with a specific _id from an array of characters that is inside a document of model Drama.

The document I'm working with:-

{
    "_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
    "characters" : [ 
        {
            "_id" : ObjectId("619fdac5a03c8b10d0b8b13c"),
            "age" : "23",
            "name" : "Vinay", 
        }, 
        {
            "_id" : ObjectId("619fe1d53810a130207a409d"),
            "age" : "25",
            "name" : "Raghu", 
        }, 
        {
            "_id" : ObjectId("619fe1d53810a130207a502v"),
            "age" : "27",
            "name" : "Teju", 
        }
    ],
}

So to update the character Raghu I did this:-

const characterObj = {
  age: "26",
  name: "Dr. Raghu",
};

Drama.updateOne(
  { _id: req.drama._id, "characters._id": characterId },
  {
    $set: {
      "characters.$": characterObj,
    },
  },
  function(err, foundlist) {
    if (err) {
      console.log(err);
    } else {
      console.log("Update completed");
    }
  }
);

// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")

This updated the character but it also assigned a new ObjectId to the _id field of the character. So, I'm looking for ways on how to prevent the _id update.

Also, I know I can set the individual fields of character instead of assigning a whole new object to prevent that but it will be very tedious if my character's object has a lot of fields.

//Not looking to do it this way
$set: {
        "characters.$.age": characterObj.age,
        "characters.$.name": characterObj.name,
      },

Thanks.

CodePudding user response:

I found something here, just pre define a schema (a blueprint in a way) that affects the id

var subSchema = mongoose.Schema({
    //your subschema content
},{ _id : false });

Stop Mongoose from creating _id property for sub-document array items

Or I would say, when you create a character assign it a custom id from the start, that way it will retain that id throughout.

CodePudding user response:

I'm leaving this question open as I would still like to see a simpler approach. But for now, I did find one easy alternative solution for this issue which I'm will be using for some time now until I find a more direct approach.

In short - Deep merge the new object in the old object using lodash and then use the new merged object to set field value.

For example, let's update the character Raghu from my question document:-

  1. First install lodash(Required for deep merging objects) using npm:
$ npm i -g npm
$ npm i --save lodash
  1. Import lodash:
const _ = require("lodash");
  1. Now update the character Raghu like this:-
const newCharacterObj = {
  age: "26",
  name: "Dr. Raghu",
};

Drama.findById(
  { _id: req.drama._id, "characters._id": characterId },
  "characters.$",
  function(err, dramaDocWithSpecificCharacter) {
    console.log(dramaDocWithSpecificCharacter);
    // ↓↓↓ console would log ↓↓↓
    // {
    //     "_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
    //     "characters" : [
    //         {
    //             "_id" : ObjectId("619fe1d53810a130207a409d"),
    //             "age" : "25",
    //             "name" : "Raghu",
    //         }
    //     ],
    // }

    const oldCharacterObj = dramaDocWithSpecificCharacter.characters[0];

    const mergedCharacterObjs = _.merge(oldCharacterObj, newCharacterObj);
    // _.merge() returns a deep merged object
    console.log(mergedCharacterObjs);
    // ↓↓↓ console would log ↓↓↓
    // {
    //   _id: 619fe1d53810a130207a409d,
    //   age: "26",
    //   name: "Dr. Raghu",
    // };

    Drama.updateOne(
      { _id: req.drama._id, "characters._id": characterId },
      {
        $set: {
          "characters.$": mergedCharacterObjs,
        },
      },
      function(err, foundlist) {
        if (err) {
          console.log(err);
        } else {
          console.log("Update completed");
        }
      }
    );
  }
);

// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")

Note: We can also use the native Object.assign() or (spread operator) to merge objects but the downside of it is that it doesn’t merge nested objects which could cause issues if you later decide to add nested objects without making changes for deep merge.

  • Related