Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What am I doing wrong in this Mongoose unique pre-save validation?

Tags:

I'm trying to create a User model, that has an unique username. Here's the code for it:

var mongoose = require("mongoose");  var Schema = mongoose.Schema;  var UserSchema = new Schema({     username: String,     password: String, });  UserSchema.virtual("password_confirmation").get(function() {     return this.pw_conf; }).set(function(value) {     this.pw_conf = value; });  UserSchema.path("username").required(true); UserSchema.path("password").required(true);  UserSchema.pre("save",function(next, done) {     var self = this;     mongoose.models["User"].findOne({username : self.username},function(err, user) {         if(user) {             self.invalidate("user","username must be unique");         }         done();     });     next(); });  UserSchema.pre("save",function(next) {     if(this.pw_conf !== this.password) {         next(new Error("Must specify the password confirmation"));     }     else {         next();     } });  module.exports = mongoose.model("User",UserSchema); 

I was also testing to see if the uniqueness works:

var User = require("./users"),   mongoose = require("mongoose"); var u = new User();  mongoose.connect('mongodb://localhost/my_database');  u.username = "me"; u.password = "password"; u.password_confirmation = "password"; u.save(function(err) {     if(err) {         console.log(err);     }     mongoose.disconnect(); }); 

Problem is, it doesn't. Each time I run the code, I get a new object created. I am aware that there are probably other ways of ensuring uniqueness, but I'd like to do it in this way. Shouldn't I be calling done after I handle the result of the findOne method? Am I calling next wrong?

like image 848
Geo Avatar asked Aug 08 '12 20:08

Geo


People also ask

What does Mongoose unique validator do?

mongoose-unique-validator is a plugin which adds pre-save validation for unique fields within a Mongoose schema. This makes error handling much easier, since you will get a Mongoose validation error when you attempt to violate a unique constraint, rather than an E11000 error from MongoDB.

What is pre save in mongoose?

The save() function triggers validate() hooks, because mongoose has a built-in pre('save') hook that calls validate() . This means that all pre('validate') and post('validate') hooks get called before any pre('save') hooks.

What is validation in mongoose?

Validation is defined in the SchemaType. Validation is middleware. Mongoose registers validation as a pre('save') hook on every schema by default. You can disable automatic validation before save by setting the validateBeforeSave option. You can manually run validation using doc.

Is Mongoose validation customizable?

Validation is an important part of the mongoose schema. Along with built-in validators, mongoose also provides the option of creating custom validations.


2 Answers

http://mongoosejs.com/docs/api.html#schematype_SchemaType-unique is the way to go. It uses actual MongoDb indexes to make sure that your field is unique. No need for .pre middleware.

Enjoy!

like image 65
rdrey Avatar answered Nov 22 '22 14:11

rdrey


To use parallel middleware (with next and done parameters), you need to pass true as the second parameter.

Beyond that, there are two possibilities:

Your self.invalidate call should be referencing "username" instead of "user". If that doesn't fix it, you can fail things explicitly by passing an Error object to done if you want to abort the save operation:

UserSchema.pre("save", true, function(next, done) {     var self = this;     mongoose.models["User"].findOne({username: self.username}, function(err, user) {         if(err) {             done(err);         } else if(user) {             self.invalidate("username", "username must be unique");             done(new Error("username must be unique"));         } else {             done();         }     });     next(); }); 
like image 23
JohnnyHK Avatar answered Nov 22 '22 15:11

JohnnyHK