skip to Main Content

I am trying to make my user model not care about case sensitivity in email and username fields. So if I try to create a user with email [email protected] and another one with [email protected], the second time it is not allowed, since the email is not unique, despite the upper case and lowercases.

My user schema is the following:

const UserSchema = new mongoose.Schema({
  email: {
    type: String,
    unique: true,
    required: true,
    validate: // validators here,
  },
  username: {
    type: String,
    unique: true,
    required: true,
    validate: // validators here,
  },
  // Some other fields not case insensitive
});

The users are created with mongoose create function:

user = await User.create(userData)

I have tried creating a collation and it works with the find command directly on mongo shell.

db.users.createIndex({'email': 1}, {'collation': { 'locale': 'en', 'strength': 2}, 'name': 'testcollation'})
db.users.find({'email':'[email protected]'}).collation({'locale':'en', 'strength': 2})

But when I try to append the collation to the create call, I get an error message saying collation function does not exist.

user = await User.create(userData).collation({ "locale": "en", "strength": 2 });

Following this link, https://mongoosejs.com/docs/api.html#query_Query-collation, I would expect this to work:

User.create(...).collation is not a function

Any idea how to run the collation with the create function?

4

Answers


  1. you cannot use collation with create since it only works with a find cursor

    Specifies the collation for the cursor returned by the db.collection.find(). To use, append to the db.collection.find()

    For your scenario you may want to use a pre save hook

    e.g.

    schema.pre("save", function(next) {
      // ...do something with "this"
      next();
    });
    
    Login or Signup to reply.
  2. Personally, I prefer to query first, create or error if there’s a duplicate. In your case, I would use a simple regex to check the case insensitivity.

    const checkUserDuplicate = await User.find({ email: { $regex: `${userData.email}`, $options: 'i' }});
    if (checkUserDuplicate && checkUserDuplicate.email === userData.email) {
      return next(res.status(500).json('Email already in use'));
    } else {
      const user = await User.create(userData);
      res.status(201).json(user);
    }
    

    According to the docs this type of middleware should run after save.

    UserSchema.post('save', function (error, doc, next) {
      if (error.name === 'MongoServerError' && error.code === 11000) {
        next(console.log('Email address already in use'))
      }
      else {
        next()
      }
    })
    

    You could then add lowercase: true to the user.email schema property which always calls .toLowerCase() on the value.

    Login or Signup to reply.
  3. To run collation with create command in mongoose, you need to specify the collation option in the schema definition or in the create method. Collation is a feature that allows you to specify the rules for comparing strings in different languages and alphabets. For example, you can use collation to make your user model not care about case sensitivity in email and username fields.

    Explanation

    Mongoose is a popular object data modeling (ODM) library for MongoDB, a NoSQL database. Mongoose allows you to define schemas for your collections, which are like blueprints for your documents. Schemas can have various options, such as validators, indexes, hooks, virtuals, and collation.

    Collation is an option that lets you define the language-specific rules for sorting and matching strings. For example, you can use collation to make your user model ignore the case of the email and username fields, so that [email protected] and [email protected] are considered the same. You can also use collation to sort strings according to different alphabets, such as Arabic, Cyrillic, or Latin.

    To use collation in mongoose, you need to specify the collation option in the schema definition or in the create method. The collation option is an object that has several properties, such as locale, strength, caseLevel, and caseFirst. You can find the full list of collation properties and their meanings in the MongoDB documentation: https://docs.mongodb.com/manual/reference/collation/

    For example, to make your user model not care about case sensitivity in email and username fields, you can use the following collation option:

    collation = {
      'locale': 'en', # use English as the language
      'strength': 2, # ignore case and diacritics, such as accents
      'caseLevel': False, # do not distinguish between base characters and case variants
      'caseFirst': 'off' # do not sort uppercase before lowercase or vice versa
    }
    

    You can then apply this collation option to your user schema or to your create method. For example:

    # define the user schema with the collation option
    userSchema = mongoose.Schema({
      email: {type: String, required: True, unique: True},
      username: {type: String, required: True, unique: True},
      password: {type: String, required: True}
    }, {collation: collation})
    
    # create the user model from the schema
    User = mongoose.model('User', userSchema)
    
    # create a new user with the collation option
    User.create({
      email: '[email protected]',
      username: 'Thisuser',
      password: '123456'
    }, {collation: collation}, (err, user) => {
      if (err) {
        console.error(err)
      } else {
        console.log(user)
      }
    })
    

    Examples

    Here are some examples of how collation works in mongoose:

    • If you create a user with email [email protected] and username Thisuser, and then try to create another user with email [email protected] and username thisuser, you will get a duplicate key error, because the collation option makes them equivalent.

    • If you create a user with email [email protected] and username Thisuser, and then try to find them by email [email protected] or username thisuser, you will get the same user, because the collation option makes them match.

    • If you create a user with email [email protected] and username Thisuser, and then try to sort them by email or username, you will get the same order regardless of the case, because the collation option makes them equal.

    Login or Signup to reply.
  4. If you create a unique index on email with that collation, that should disallow creating duplicate emails at the database level:

    db.users.createIndex({'email': 1}, { 'collation': { 'locale': 'en', 'strength': 2 }, unique: true, 'name': 'testcollation' })

    Creating this index should make your duplicate User.create calls fail. The Mongoose Schema unique option creates a similar index automatically, but it won’t be aware of your desired collation. I’m unsure whether there’s a way of setting up Mongoose schema settings to get it to create your desired index.

    You can also set the collation on the collection level in your createCollection call. You can’t modify an existing collection to have a different collation though: this will only work on initial collection creation.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search