Solve the error report of MongoServerError: E11000 duplicate key error collection in Mongoose

Background

Recently, I was developing a personal blog project based on express + mongoose, but this error occurred when adding new users:

MongoServerError: E11000 duplicate key error collection: test.users index: username_1 dup key: {<!-- --> username: "123abc" }

Error analysis

duplicate key error collection means that a duplicate key error has occurred in the database collection. In a database, a collection is a container for storing a group of related documents, and a key is a field that uniquely identifies each document.
A duplicate key error is triggered when attempting to insert a new document into a database collection that contains a key with the same value as a key in an existing document.

Then it is easy to know that my current error message is: In the test.users collection, a document with the value of the username field “123abc” already exists, and this field is set as a unique index. The same is true.

Because my project requirement is: when a user registers, the user name is not allowed to be repeated and is unique, so I set the unique: true attribute for the username in the Schema.

When I register a user for the first time, the user’s information can be printed normally.

When I run the code again (that is, register again with the same user information), the problem of duplicate keys will appear at this time. That is, the above-mentioned error occurred.

Solution

The current business scenario is user registration, and user names are not allowed to be repeated (this is stipulated in my project). There are several workarounds:

  • Modify the value of the field: that is, the document has a username value of “123abc”, then you need to modify the value of username, which cannot be “123abc”;
  • Update existing documents: use the updateOne() or updateMany() method to update documents by query conditions;
  • Delete an existing document: You can use the deleteOne() or deleteMany() method to delete an existing document, and then insert a new document.

Although the above method can solve the problem, it is inevitable that the user name will be repeated when the user registers. Once this situation occurs, an error will be reported, the program will terminate, and the user will not be able to register again. What we have to do is to deal with this error, and tell the user that the reason for the duplicate user name is that they cannot register, and prompt the user to change a username.

Final solution:

Because it is developed with mongoose, we can go to the mongoose documentation to see if there is any handling of this error. The key information of the error message is duplicate key error, and I was using Validation to verify the SchemaType at the time, and it just happened that unique is not a validator in the document , then it will inevitably give error handling when using unique.

The document gives how to deal with duplicate key error, and inspired by this, I dealt with duplicate key error.

U2.init().
  then(() => U2. create(dup)).
  catch(error => {<!-- -->
    // `U2.create()` will error, but will *not* be a mongoose validation error, it will be
    // a duplicate key error.
    // See: https://masteringjs.io/tutorials/mongoose/e11000-duplicate-key
    assert. ok(error);
    assert.ok(!error.errors);
    assert.ok(error.message.indexOf('duplicate key error') !== -1);
  });

Use try catch to catch exception errors. There are two cases of caught errors: one is the problem of duplicate keys, and the other is the error set by mongoose’s built-in validator. The two errors are handled differently. If the query error message has duplicate key error, return false;

If not, it is an error set by mongoose’s built-in validator, and it should be further processed.


The errors set by mongoose’s built-in validator can be customized, such as here:

username: {<!-- -->
    type: String,
    required: [true, 'username required'], // required
    unique: true, // unique item
    validate: {<!-- -->
      validator(value) {<!-- -->
        return /^(?!\d + $)(?![a-zA-Z] + $)[a-zA-Z0-9]{6,8}$/.test(value)
      },
      message: props => `${<!-- -->props.value} is not a valid username!`
    }
  }

Complete demo code:

let schema = new mongoose.Schema({<!-- -->
  username: {<!-- -->
    type: String,
    required: [true, 'username required'], // required
    unique: true, // unique item
    validate: {<!-- -->
      validator(value) {<!-- -->
        return /^(?!\d + $)(?![a-zA-Z] + $)[a-zA-Z0-9]{6,8}$/.test(value)
      },
      message: props => `${<!-- -->props.value} is not a valid username!`
    }
  },
  // The password does not need to add validate verification conditions, because the password sent by the front end is encrypted
  passowrd: {<!-- -->
    type: String,
    select: false,
    required: [true, 'passowrd required'],
    set(value) {<!-- --> // encrypted password
      return encrypt(value)
    }
  },
  email: {<!-- -->
    type: String,
    unique: true,
    validate: {<!-- -->
      validator(value) {<!-- -->
        return /^[a-z0-9] + ([._\-]*[a-z0-9])*@([a-z0-9] + [-a-z0-9]* [a-z0-9] + .){1,63}[a-z0-9] + $/.test(value)
      },
      message: props => `${<!-- -->props.value} is not a valid email!`
    }
  },
  avatar: {<!-- -->
    type: String,
    default: "http://127.0.0.1:3000/public/images/avatar.jpg"
  },
  nikname: {<!-- -->
    type: String,
    validate: {<!-- -->
      validator(value) {<!-- -->
        return /^([\w\W]){1,8}$/.test(value)
      },
      message: props => `${<!-- -->props.value} is not a valid nikname!`
    },
    default: "user"
  }
})

let User = mongoose. model('User', schema)
User.create({<!-- -->
  username: "123abc",
  passowrd: "1234cdf",
  email: "[email protected]"
}).then(data => {<!-- -->
  console. log(data);
}).catch(err => {<!-- -->
  // console.log(err.message); // E11000 duplicate key error collection: test.users index: username_1 dup key: { username: "123abc" }
  if (err. message. indexOf('duplicate key error') !== -1) {<!-- -->
    console.log('there are duplicate keys', err.keyPattern);
    return false
  }
  Object.entries(err).map(([key, value]) => {<!-- -->
    console.log(`error: ${<!-- -->key}, ${<!-- -->value.message} `)
  })
})

Here is the function of simulating user registration. The error handling in it is not very detailed, but if it is actually used in the project, the error information will be handled more carefully.