Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose: How to prevent mongodb to save duplicate email records in database

I want to make the key email unique across that collection but i cant getting this working, here is my server code.

// Create a schema
var userSchema = new mongoose.Schema({
    email: { type: String, required: true},
    password: String
});

var userModel = mongoose.model("user", userSchema);

router.post('/postuser', (req, res) => {
    console.log('Requested data to server: ' + JSON.stringify(req.body._user));
var user = new userModel({
    email: req.body._user.email,
    password: req.body._user.password
});
// user.isNew = false;
user.save((err, data) => {
    console.log('Analyzing Data...');
    if(data) {
        console.log('Your data has been successfully saved.');
        res.json(data);
}
else {
  console.log('Something went wrong while saving data.');
  console.log(err);
  res.send(err);
}

})
});

Note: I also try email: { type: String, required: true, unique: true} but its not working and show below error.

name: 'MongoError',

message: 'E11000 duplicate key error collection: hutreservationsystem.users

index: _Email_1 dup key: { : null }',

driver: true,

code: 11000,

index: 0,

errmsg: 'E11000 duplicate key error collection: hutreservationsystem.users index: _Email_1 dup key: { : null }',

getOperation: [Function],

toJSON: [Function],

toString: [Function] }

like image 573
Ahmer Ali Ahsan Avatar asked May 14 '17 09:05

Ahmer Ali Ahsan


3 Answers

Async Custom Validator

var userSchema = new mongoose.Schema({
    password: String,
    email: {
        type: String,
        lowercase: true,
        required: true,
        validate: {
            isAsync: true,
            validator: function(value, isValid) {
                const self = this;
                return self.constructor.findOne({ email: value })
                .exec(function(err, user){
                    if(err){
                        throw err;
                    }
                    else if(user) {
                        if(self.id === user.id) {  // if finding and saving then it's valid even for existing email
                            return isValid(true);
                        }
                        return isValid(false);  
                    }
                    else{
                        return isValid(true);
                    }

                })
            },
            message:  'The email address is already taken!'
        },
    }
});

You may like to change the validator code to es6.

like image 152
Talha Awan Avatar answered Sep 29 '22 00:09

Talha Awan


A short answer using this tool mongoose-unique-validator

npm install --save mongoose-unique-validator

and in your model

var mongoose = require('mongoose')
var uniqueValidator = require('mongoose-unique-validator')
var userSchema = new mongoose.Schema({
    email: { type: String, required: true, unique: true},
    password: String
});

userSchema.plugin(uniqueValidator)
var userModel = mongoose.model("user", userSchema);

That's it! (Notice unique: true)

Now, there is no email duplication in your collection.

Bonus! : you can access err

.catch(err => console.log(err))

so in your example

// user.isNew = false;
user.save((err, data) => {
    console.log('Analyzing Data...');
    if(data) {
        console.log('Your data has been successfully saved.');
        res.json(data);
}
else {
  console.log('Something went wrong while saving data.');
  console.log(err);
  res.send(err);
}

accessing err >> so you can res.send(err.message) >> 'Validation failed'

{
    message: 'Validation failed',
    name: 'ValidationError',
    errors: {
        email: {
            message: 'Error, expected `email` to be unique. Value: `[email protected]`',
            name: 'ValidatorError',
            kind: 'unique',
            path: 'email',
            value: '[email protected]'
        }
    }
    }
like image 24
Ahmed Younes Avatar answered Sep 28 '22 23:09

Ahmed Younes


email: {
    type: String,
    trim: true,
    unique: true, // note - this is a unqiue index - not a validation
    validate: {
        validator: function(value) {
            const self = this;
            const errorMsg = 'Email already in use!';
            return new Promise((resolve, reject) => {
                self.constructor.findOne({ email: value })
                    .then(model => model._id ? reject(new Error(errorMsg)) : resolve(true)) // if _id found then email already in use 
                    .catch(err => resolve(true)) // make sure to check for db errors here
            });
        },
    }
},
like image 43
Aditya Samanta Avatar answered Sep 29 '22 00:09

Aditya Samanta