Reputation: 3520
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.
Upvotes: 35
Views: 16684
Reputation: 800
The reason behind this behavior is that mongoose assumes you are just going to update the document, not insert one. The only possibility of having an invalid model with upsert is therefore to perform an $unset
. In other words, findOneAndUpdate
would be appropriate for a PATCH
endpoint.
If you want to validate the model on insert, and be able to perform a update on this endpoint too (it would be a PUT
endpoint) you should use replaceOne
Upvotes: 1
Reputation: 1
If you want to validate with findOneAndUpdate you can not get current document but you can get this keywords's contents and in this keywords's content have "op" property so solution is this :
Note : does not matter if you use context or not. Also, don't forget to send data include both "price" and "priceDiscount" in findOneAndUpdate body.
validate: {
validator: function (value) {
if (this.op === 'findOneAndUpdate') {
console.log(this.getUpdate().$set.priceDiscount);
console.log(this.getUpdate().$set.price);
return (
this.getUpdate().$set.priceDiscount < this.getUpdate().$set.price
);
}
return value < this.price;
},
message: 'Discount price ({VALUE}) should be below regular price',
}
Upvotes: 0
Reputation: 119
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.
Upvotes: 4
Reputation: 4931
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) {
// ...
}
Upvotes: 7
Reputation: 117
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.
Upvotes: 10
Reputation: 12062
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);
});
Upvotes: 1
Reputation: 513
In mongoose do same thing in two step.
findOne()
method.Model.save()
.This will update your document.
Upvotes: 4