Home > Mobile >  Mongoose throws E11000 duplicate key error when updating a document with .save() method
Mongoose throws E11000 duplicate key error when updating a document with .save() method

Time:12-21

I have a user model as shown below:

const userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true,
        minlength: 3,
        maxlength: 30,
        validate: {
            validator: function(v) {
                return /^[a-zA-Z0-9] $/.test(v);
            },
            message: "Your user name must be alphanumeric."
        },
        unique: true
    },
    email: {
        type: String,
        required: true,
        validate: {
            validator: function(v) {
                return /(?:[a-z0-9!#$%&'* /=?^_`{|}~-] (?:\.[a-z0-9!#$%&'* /=?^_`{|}~-] )*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.) [a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f]) )\])/.test(v);
            },
            message: "Invalid e-mail address."
        },
        unique: true
    },
    password: {
        type: String,
        required: true,
        minlength: 4,
        maxlength: 1024
    },
    isAdmin: {
        type: Boolean,
        default: false
    },
    devices: [{
        type: mongoose.SchemaTypes.ObjectId,
        ref: 'Device'
    }],
    joinDate: {
        type: Date,
        default: Date.now
    }
});

const User = mongoose.model('User', userSchema);

I have a users.js Express.js router to manage users. One of these routes update the existing user with specified user ID. Here's the route:

// Modify a user's profile
router.put('/:userId', [auth, authAdmin], async function(req, res, next) {
    if(!isValidObjectID(req.params.userId)) return res.status(400).send({ message: 'Given ID is not valid.', status: 400 });

    const { error } = validate(req.body);
    if(error) return res.status(400).send({ message: error.details[0].message, status: 400 });

    let user = await User.findOne({ email: req.body.email });
    if(user && user._id && user._id != req.params.userId) return res.status(400).send({ message: 'E-mail address is already in use.', status: 400 });

    user = await User.findOne({ username: req.body.username });
    if(user && user._id && user._id != req.params.userId) return res.status(400).send({ message: 'Usename is already in use.', status: 400 });

    user = await User.findById(req.user._id);

    user.username = req.body.username;
    user.email = req.body.email;
    if(req.body.isAdmin) user.isAdmin = req.body.isAdmin;
    const salt = await bcrypt.genSalt(10);
    user.password = await bcrypt.hash(req.body.password, salt);

    try {
        user = await user.save();
        return res.send(_.omit(user.toObject(), 'password'));
    } catch(exception) {
        console.log('Put 1:', exception);
    }
});

When I use this route to update an existing user's only username I get MongoServerError: E11000 duplicate key error collection: iotapi.users index: email_1 dup key: { email: "[email protected]" } error. There's something which doesn't make sense. I also have another route just for users to update their email addresses. That route does almost the same functionality except updating username. It works very well, but when I update username along with the email, it throws the error.

I tried to use .findOneByIdAndUpdate() method as well to update documents but It didn't work out. I got the same error.

CodePudding user response:

There is a typo

user = await User.findById(req.user._id);

Should be

user = await User.findById(req.params.userId);

update

Ok, not a typo but a genuine mistake then.

In the condition

let user = await User.findOne({ email: req.body.email });
if(user && user._id && user._id != req.params.userId) 

You return 400 only when user with given email exists and its id differs from the Id send in the query string. In other words, when Ids are the same the code continues.

Then you reach the line where the user is loaded from auth session:

user = await User.findById(req.user._id);

This id can be different from the one sent in the request, so you try to update it with email of the other user. It cause duplication error.

  • Related