wellowlbe
wellowlbe

Reputation: 25

Able to create multiple users with same email despite email set as unique in Mongoose

I am writing an API using Express, MongoDB, and Mongoose. I am somehow able to create multiple users with the same email. However, I shouldn't be able to create another user with the same email address. I have email unique: true in my user schema, but this isn't working as expected.

Here's my user schema:

var UserSchema = new Schema({
fullName: { type: String, required: [true, 'Full name is required.'] },
emailAddress: {
    type: String, required: true, unique: true,
    validate: {
        validator: function (value) {
            // check for correct email format
            return /^[a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(value)
        },
        message: `Please enter a valid email address!`
    }
},
password: { type: String, required: true }
});

My user authenticate method:

UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ emailAddress: email })
    .exec(function (err, user) {
        if (err) {
            return callback(err);
        } else if (!user) {
            var error = new Error('User not found');
            error.status = 401;
            return callback(error);
        }
        bcrypt.compare(password, user.password, function (error, user) {
            if (user) {
                return callback(null, user);
            } else {
                return callback();
            }
        });
    });
}

My pre-save hook to hash the password:

UserSchema.pre('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
    if (err) {
        return next(err);
    }
    user.password = hash;
    next();
});
});

And finally, my user create route:

router.post('/', function (req, res, next) {
if (req.body.fullName &&
    req.body.emailAddress &&
    req.body.password &&
    req.body.confirmPassword) {

    if (req.body.password != req.body.confirmPassword) {
        var err = new Error('Passwords do not match!');
        err.status = 400;
        return next(err);
    }

    // object with form input
    var userData = {
        fullName: req.body.fullName,
        emailAddress: req.body.emailAddress,
        password: req.body.password
    };

    // schema's 'create' method to insert document into Mongo
    User.create(userData, function (error, user) {
        if (error) {
            var err = new Error('Please enter a valid email.');
            err.status = 400;
            return next(err);
        } else {
            // set location header to '/', return no content
            res.status(201);
            res.location('/');
            req.session.userId = user._id;
            return res.json(user);
        }
    });

} else {
    var err = new Error('All fields required.');
    err.status = 400;
    return next(err);
}
});

Upvotes: 0

Views: 1230

Answers (1)

Tanmay Awasekar
Tanmay Awasekar

Reputation: 46

MongoDB does not create unique index to a field if the field value has duplicates already stored in it.

In your case emailAddress must have duplicates already stored in the database. You can check it by runing the code

mongoose .model(#modelName) .collection.createIndex( { "inviteCode": 1 });

After running this code you should be able to see a error message in the console.

Or You can check by running the below code if you have duplicate. The below will fetch documents if have duplicates:

mongoose .model(#modelName).aggregate([{ "$group": { "_id": "$loginStatus", count: { $sum: 1 }, ids: { $push: "$_id" } } },{ $match: { count: { $gt : 1 } }}]);

If you emailAddress which are already duplicates you cant create unique: true on it. You would have to run the second query and find out the duplicate email address. You can find the documents with duplicate email in the ids array.

Upvotes: 1

Related Questions