Leestex Leestex - 3 months ago 29
Javascript Question

Mongoose custom validation of several fields on update

First of all, this doesn't help.

Let's say, we have a User model:

const schema = new mongoose.Schema({
active: { type: Boolean },
avatar: { type: String }
});

const User = mongoose.model('User', schema);


When we update it (set an avatar):

// This should pass validation
User.update({ _id: id }, { $set: { avatar: 'user1.png' } });


We want to validate it based on current (or changed)
active
attribute value.

Case #1




  • active
    is
    false

  • we should not be able to set avatar - it should not pass the validation



Case #2




  • active
    is
    true

  • we should be able to set avatar - it should pass the validation



Ideas




  1. Use a custom validator



const schema = new mongoose.Schema({
active: { type: Boolean },
avatar: { type: String, validate: [validateAvatar, 'User is not active'] }
});

function validateAvatar (value) {
console.log(value); // user.avatar
console.log(this.active); // undefined
}


So this will not work as we don't have an access to
active
field.


  1. Use pre "validate" hook



schema.pre('validate', function (next) {
// this will never be called
});


This hook doesn't work with
update
method.


  1. Use pre "update" hook



schema.pre('update', function (next) {
console.log(this.active); // undefined
});


This will not work for us as it doesn't have an access to model fields.


  1. Use post "update" hook



schema.post('update', function (next) {
console.log(this.active); // false
});


This one works, but in terms of validation is not quite good choice, as the function is being called only when model was already saved.

Question



So is there a way to validate the model based on several fields (both saved in DB and new ones) before saving it, while using
model.update()
method?

As a summary:


  1. Initial user object



{ active: false, avatar: null }



  1. Update



User.update({ _id: id }, { $set: { avatar: 'user1.png' } });



  1. Validation should have an access to



{ active: false, avatar: 'user1.png' }



  1. If validation fails, changes should not be passed to DB


Answer

Due to limitation of working with update() I've decided to solve the problem this way:

  • Use custom validators (idea #1 mentioned in the question)
  • Don't use update()

So instead of

User.update({ _id: id }, { $set: { avatar: 'user1.png' } });

I use

User.findOne({ _id: id })
    .then((user) => {
        user.avatar = 'user1.png';
        user.save();
    });

In this case custom validators work as expected.

P.S. I choose this answer as a correct one for me, but I will give bounty to the most relevant answer.