Jdruwe:
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}
});
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.
vkarpov15:
solve proble:
var assert = require('assert');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.set('debug', true);
mongoose.connect('mongodb://localhost:27017/gh3124');
var userSchema = new mongoose.Schema({
facebookId: {type: Number, required: true},
activated: {type: Boolean, required: true, default: false},
email: {type: String, required: true}
});
var User = mongoose.model('gh3124', userSchema);
User.findOneAndUpdate(
{facebookId: 123},
{
$setOnInsert: {
facebookId: 123,
email: undefined, // mongoose really has no way of knowing if the documents being updated on the server side have an email field or not
}
},
{upsert: true,
'new': true,
runValidators: true,
setDefaultsOnInsert: true
}, function (err, user) {
console.log(err);
process.exit(0);
});
Ah yeah that’s a good point. Update validators right now only run on fields that are mentioned in the update (and the query, if upsert is specified), because mongoose really has no way of knowing if the documents being updated on the server side have an email field or not. There are definitely ways to be smarter about this, but right now update validators are somewhat limited.
jeremyml:
I created a plugin to validate required model properties before doing update operations in mongoose.
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.
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);
});