32
votes

I am having issues trying to get the 'runValidators' option to work. My user schema has an email field that has required set to true but each time a new user gets added to the database (using the 'upsert' option) and the email field is empty it does not complain:

 var userSchema = new mongoose.Schema({
   facebookId: {type: Number, required: true},
   activated: {type: Boolean, required: true, default: false},
   email: {type: String, required: true}
});

findOneAndUpdate code:

model.user.user.findOneAndUpdate(
      {facebookId: request.params.facebookId},
      {
          $setOnInsert: {
              facebookId: request.params.facebookId,
              email: request.payload.email,
          }
      },
      {upsert: true, 
       new: true, 
       runValidators: true, 
       setDefaultsOnInsert: true
      }, function (err, user) {
          if (err) {
              console.log(err);
              return reply(boom.badRequest(authError));
          }
          return reply(user);
      });

I have no idea what I am doing wrong, I just followed the docs: http://mongoosejs.com/docs/validation.html

In the docs is says the following:

Note that in mongoose 4.x, update validators only run on $set and $unset operations. For instance, the below update will succeed, regardless of the value of number.

I replaced the $setOnInsert with $set but had the same result.

5

5 Answers

8
votes

required validators only fail when you try to explicitly $unset the key.

This makes no sense to me but it's what the docs say.

5
votes

use this plugin: mongoose-unique-validator

When using methods like findOneAndUpdate you will need to pass this configuration object:

{ runValidators: true, context: 'query' }

ie.

User.findOneAndUpdate(
  { email: '[email protected]' },
  { email: '[email protected]' },
  { runValidators: true, context: 'query' },
  function(err) {
    // ...
}
4
votes

In mongoose do same thing in two step.

  1. Find the result using findOne() method.
  2. Add fields and save document using Model.save().

This will update your document.

2
votes

I fixed the issue by adding a pre hook for findOneAndUpdate():

ExampleSchema.pre('findOneAndUpdate', function (next) {
    this.options.runValidators = true
    next()
})

Then when I am using findOneAndUpdate the validation is working.

1
votes

I created a plugin to validate required model properties before doing update operations in mongoose.

Plugin code here

var mongoose = require('mongoose');
var _ = require('lodash');
var s = require('underscore.string');

function validateExtra(schema, options){
    schema.methods.validateRequired = function(){
        var deferred = Promise.defer();
        var self = this;
        try {
            _.forEach(this.schema.paths, function (val, key) {
                if (val.isRequired && _.isUndefined(self[key])) {
                    throw new Error(s.humanize(key) + ' is not set and is required');
                }
            });
            deferred.resolve();
        } catch (err){
            deferred.reject(err);
        }
        return deferred.promise;
    }
}

module.exports = validateExtra;

Must be called explicitly as a method from the model, so I recommend chaining it a .then chain prior to the update call.

Plugin in use here

fuelOrderModel(postVars.fuelOrder).validateRequired()
    .then(function(){
        return fuelOrderModel.findOneAndUpdate({_id: postVars.fuelOrder.fuelOrderId}, 
           postVars.fuelOrder, {runValidators: true, upsert: true, 
           setDefaultsOnInsert: true, new: true})
                .then(function(doc) {
                    res.json({fuelOrderId: postVars.fuelOrder.fuelOrderId});
                });
    }, function(err){
            global.saveError(err, 'server', req.user);
            res.status(500).json(err);
    });