3

Im trying to create multiple accounts.

The first account always works, but when im trying to make a new one i get the following error:

BulkWriteError: insertDocument :: caused by :: 11000 E11000 duplicate key error index: db.users.$friends.userid_1  dup key: { : null }

The first user is fine, containing friends with an empty array, as i would like it to be.

But the next user is not created.

What do i have to do to fix this error?

The user schema piece of friends in users is the following:

   friends : [
    {
        userid : {type: String, default: '', unique: true },
    }
],
friendRequests: [
    {
        userid : {type: String, default: '', unique: true },
    }

EDIT:

i have been looking into https://docs.mongodb.com/manual/core/index-unique/#unique-index-and-missing-field but still couldnt make it work.

EDIT2:

By default, its not creating any friends or friendrequests.

EDIT3:

full code:

passport.use('local-signup', new LocalStrategy({
            usernameField : 'username',
            passwordField : 'password',
            passReqToCallback : true,
        },
        function(req, username, password, done) {
            process.nextTick(function() {
                console.log("doing local signup");
                username = username.toLowerCase();
                Account.findOne({username :  username }, function(err, user) {
                    var expr = "/admin/";
                    if (err) {
                        return done(err);
                    } else if (user) {
                        return done(null, false, 'That username is already taken.');
                    } else if(username.length < 3 || username.length >= 12) {
                        return done(null, false, 'Username has to be between 3 and 12 characters! :( '  + username);
                    } else if(/^[a-zA-Z0-9- ]*$/.test(username) == false) {
                        return done(null, false, 'You cant have any special characters!');
                    } else if(password.length < 5 || password.length > 15) {
                        return done(null, false, 'Password need to be 5-15 characters long!');
                    } else {

                        var newUser            = new Account();
                        newUser.username    = username;
                        newUser.password = newUser.encryptPassword(password);
                        newUser.save(function(err) {
                            if (err)
                                throw err;
                            return done(null, newUser);
                        });
                    }
                });

            });

        }));

user model:

var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var bcrypt   = require('bcrypt-nodejs');



var UserSchema   = new Schema({
    username: {type: String, index: { unique: true }},
    password: {type: String},
    salt: { type: String},
    hash: {type: String},
    gender : {type: String, default: 'male'},
    friends : [
        {
            userid : {type: String, default: '', unique: true },
        }
    ],
    friendRequests: [
        {
            userid : {type: String, default: '', unique: true },
        }
    ]

});
UserSchema.methods.encryptPassword = function(password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(10));
}

UserSchema.methods.validPassword = function(password) {
    return bcrypt.compareSync(password, this.password);
}



module.exports = mongoose.model('Users', UserSchema);
maria
  • 207
  • 5
  • 22
  • 56
  • Are you sure you need a unique index for `friends.userid` ? It means a person can be a friend of no more than 1 other person. You are modelling quite a lonely world then. – Alex Blex Feb 15 '18 at 12:40
  • oh , you are right @AlexBlex , my idea was that the userid has to be unique tho. – maria Feb 15 '18 at 12:40
  • That's fair restriction. People are quite unique individuals. The unique constraint should be on the user schema on id property then. If you are using default `_id` it is already unique. – Alex Blex Feb 15 '18 at 12:43
  • delete previously created indexes? – Alex Blex Feb 15 '18 at 12:47
  • @AlexBlex its updated now. see previous comment – maria Feb 15 '18 at 12:47
  • Can you add some sample documents to the post that you are inserting to reproduce the error ? Also did you look at this [answer](https://stackoverflow.com/questions/24430220/e11000-duplicate-key-error-index-in-mongodb-mongoose) ? It seems like you're trying to insert multiple documents without friends or empty friends array. Unique constraint is across collection not in same document. So you can still insert same user id in friends array in same document. Is that what you want ? – s7vr Feb 21 '18 at 13:31
  • @Veeram the thing is that while im creating this user ( code updated), im not adding anything in friends. i want it to be empty until the user get friends. so not create a array by default. – maria Feb 21 '18 at 13:40
  • Are you intending to have friends list unique across collection ? For ex: User A is friends with User B in first document and in second document User C **can't** be friends with User B as he is already friends with User A. Is that what you want ? Unique constraint doesn't enforce uniqueness in friends array inside single document i.e. User A **can** add User B multiple times as a friend in friends array. – s7vr Feb 21 '18 at 13:49
  • no. Im only instrested that its impossible for user A to have user B on user A `s list twice. – maria Feb 21 '18 at 16:03

1 Answers1

1

As noted in the comment Mongodb doesn't enforce uniqueness in array values inside single document.

So you have to handle the uniqueness in array inside the client code. You can use combination of strategy to handle the requirement for you.

First remove the unique index and use Mongoose unique array plugin to have mongoose check the uniqueness in the array when you create/update a document.

The plugin works for both scalar and document arrays.For your case you can do the following changes.

var uniqueArrayPlugin = require('mongoose-unique-array');
UserSchema.plugin(uniqueArrayPlugin);

This will enforce the validation through validator and should show you an validation message when you perform update/save operation.

As noted in the blog the unique plugin doesn't work when you use $push in update query.

You can use

Account.findOne({"friends.userid":{"$ne":inputuserid}}, {"$push":{"friends":new user doc}});

Read blog and usage examples from author for more information.

s7vr
  • 73,656
  • 11
  • 106
  • 127