Mongoose selectively check multiple fields when updating

First of all, it does not help.

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 the avatar):

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

We want to check it based on the current (or changed) value of the active attribute.

Case No. 1

  • active false
  • we should not be able to set an avatar - it should not pass verification

Case No. 2

  • active true
  • we must be able to set an avatar - it must pass the test

Ideas

  • Use a special 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, since we do not have access to the active field.

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

This method does not work with the 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 does not have access to the model fields.

  1. Use Update Error Message
 schema.post('update', function (next) { console.log(this.active); // false }); 

This works, but from the point of view of verification, it is not a good choice, since the function is called only when the model has already been saved.

Question

So, is there a way to check the model based on several fields (both stored in the database and new) before saving them using the model.update() method?

As a summary:

  • User start object
 { active: false, avatar: null } 
  1. Update
 User.update({ _id: id }, { $set: { avatar: 'user1.png' } }); 
  1. Validation must have access to
 { active: false, avatar: 'user1.png' } 
  1. If validation fails, changes should not be submitted to DB
+6
source share
5 answers

Due to the limitations of working with update() I decided to solve the problem as follows:

  • Use special validators (idea # 1 mentioned in the question)
  • Do not use update()

So instead

 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.

PS I choose this answer as the right one for me, but I will give generosity to the most relevant answer.

+3
source

You can do this with the context option specified in the mongoose documentation .

Context option

The context parameter allows you to set the value of this parameter in the settings for checking for updates to the basic request.


So in your code, you can define your validator in the path as follows:
 function validateAvatar (value) { // When running update validators with the `context` option set to // 'query', `this` refers to the query object. return this.getUpdate().$set.active; } schema.path('avatar').validate(validateAvatar, 'User is not active'); 

And when updating, you need to enter two options runValidators and context . So your upgrade request will look like this:

 var opts = { runValidators: true, context: 'query' }; user.update({ _id: id }, { $set: { avatar: 'user1.png' }, opts }); 
+1
source

You tried to give an active default value, so it will not be undefined in mongodb.

 const schema = new mongoose.Schema({ active: { type: Boolean, 'default': false }, avatar: { type: String, trim: true, 'default': '', validate: [validateAvatar, 'User is not active'] }}); function validateAvatar (value) { console.log(value); // user.avatar console.log(this.active); // undefined } 

When you create, you set the user this way

  var User = mongoose.model('User'); var user_1 = new User({ active: false, avatar: ''}); user_1.save(function (err) { if (err) { return res.status(400).send({message: 'err'}); } res.json(user_1); }); 
+1
source

You can try using the "save" hook. I have used it before and can get the value in "this".

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

Hope this work is with you too!

0
source

To do this, use an asynchronous custom validator :

 const schema = new mongoose.Schema({ active: { type: Boolean }, avatar: { type : String, validate : { validator : validateAvatar, message : 'User is not active' } } }); function validateAvatar(v, cb) { this.model.findOne({ _id : this.getQuery()._id }).then(user => { if (user && ! user.active) { return cb(false); } else { cb(); } }); } 

(and pass the runValidators and context parameters to update() as indicated in the answer from Naeem).

However, this will require an additional request for each update, which is not ideal.

Alternatively, you can also use something like this (if the restriction on the inability to update inactive users is more important than the actual confirmation for it):

 user.update({ _id : id, active : true }, { ... }, ...); 
0
source

All Articles