Mongoose Unique index not working!

Mongodbnode.jsMongoose

Mongodb Problem Overview


I'm trying to let MongoDB detect a duplicate value based on its index. I think this is possible in MongoDB, but through the Mongoose wrapper things appear to be broken. So for something like this:

User = new Schema ({
  email: {type: String, index: {unique: true, dropDups: true}}
})

I can save 2 users with the same email. Darn.

The same issue has been expressed here: https://github.com/LearnBoost/mongoose/issues/56, but that thread is old and lead to nowhere.

For now, I'm manually making a call to the db to find the user. That call is not expensive since "email" is indexed. But it would still be nice to let it be handled natively.

Does anyone have a solution to this?

Mongodb Solutions


Solution 1 - Mongodb

Oops! You just have to restart mongo.

Solution 2 - Mongodb

> Oops! You just have to restart mongo.

And re-index too, with:

mongo <db-name>
> db.<collection-name>.reIndex()

In testing, since I don't have important data, you can also do:

mongo <db-name>
> db.dropDatabase()

Solution 3 - Mongodb

I ran into the same issue: I added the unique constraint for the email field to our UserSchema after already having added users to the db, and was still able to save users with dupe emails. I resolved this by doing the following:

  1. Remove all documents from the users collection.

  2. From the mongo shell, execute the command: db.users.createIndex({email: 1}, {unique: true})

Regarding step 1, note that from Mongo's docs: >MongoDB cannot create a unique index on the specified index field(s) if the collection already contains data that would violate the unique constraint for the index.

https://docs.mongodb.com/manual/core/index-unique/

Solution 4 - Mongodb

I've done something like this:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const FooSchema = new Schema({
   name: { type: String, required: true, index: true, unique: true }
});

const Foo = mongoose.model('Foo', FooSchema);

Foo.createIndexes();

module.exports = Foo

I added the Foo.createIndexes() line b.c. I was getting the following deprecation warning when the code was being ran:

(node:21553) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.

I'm not sure if Foo.createIndexes() is asynchronous, but AFAIK things seem to be working fine

Solution 5 - Mongodb

This behavior happen also if you have left some duplicates in Mongo. Mongoose will try to create them in Mongo when your application starts up.

To prevent this, you can handle this error this way :

yourModel.on('index', function(err) {
  if (err?) {
    console.error(err)
  }
);

Solution 6 - Mongodb

Ok, i was able to resolve this from the mongoshell by adding the index on the field and setting the unique property:

db.<collectionName>.ensureIndex({fieldName: 1}, {unique: true});

Shell should respond in this way:

{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

Now to test quickly from the mongoshell:

var doc = {fieldName: 'abc'};
db.<collectionName>.insert(doc)

Should give: WriteResult({ "nInserted" : 1 })

But when repeating again:

db.<collectionName>.insert(doc)

Will give:

WriteResult({
    "nInserted" : 0,
    "writeError" : {
        "code" : 11000,
        "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: fuelConsumption.users.$email_1  dup key: { : \"[email protected]\" }"
    }
})

Solution 7 - Mongodb


Steps to fix the issue:

1 . Add unique: true to the attributes.

let schema = new mongoose.Schema(
	{
		name: {
			type: String,
			unique: true,
			required: [true, "name required."],
		}
	}
);

module.exports = mongoose.model("role", schema);

2 . Drop the collection - for example role (last line)

  • This is simple way to fix - if you have already duplicate values.
  • You can also delete all records in collection so that there are duplicate values for the unique column (name above)

3 . Restart the Node.js server, that uses mongoose library.


How some of other answers here, are not correct?

  • The autoIndex option is set to true

    • not required, by default it is true
  • Restart the db

    • not required, you only need to restart the Node.js server

  • Follow above 3 steps in sequence
    • If you miss anything, do it 2 times

Solution 8 - Mongodb

According the documentation : https://docs.mongodb.com/v2.6/tutorial/modify-an-index/

> To modify an existing index, you need to drop and recreate the index.

DO NOT RESTART MONGO !

1 - drop the collection

db.users.drop()

2 - reindex the table

db.users.ensureIndex({email: 1, type: 1}, {unique: true})

Solution 9 - Mongodb

Mongoose is a little loose when enforcing unique indices from the application level; therefore, it's preferred to either enforce your unique indices from the database itself using the mongo cli or explicitly tell mongoose that you're serious about the unique index by writing the following line of code just after your UserSchema:

UserSchema.index({ username: 1, email: 1 }, { unique: true});

This will enforce the unique index on both username and email fields in your UserSchema. Cheers.

Solution 10 - Mongodb

Mongoose will silently fail to add a unique index when either:

  1. The collection already has an index of the same name
  2. The collection already contains documents with duplicates of the indexed field

In the first case, list the indexes with db.collection.getIndexes(), and drop the old index with db.collection.dropIndex("index_name"). When you restart the Mongoose application it should correctly add the new index.

In the second case you need to remove the duplicates before restarting the Mongoose application.

Solution 11 - Mongodb

mongoose-unique-validator

How to use this plugin:

  1. npm install --save mongoose-unique-validator

  2. in your schema follow this guide:

    // declare this at the top var mongoose = require('mongoose'); var uniqueValidator = require('mongoose-unique-validator');

    // exampleSchema = mongoose.Schema({}) etc...

    exampleSchema.plugin(uniqueValidator);

    // module.exports = mongoose.model(...) etc....

  3. mongoose methods

    When using methods like findOneAndUpdate you will need to pass this configuration object:

    { runValidators: true, context: 'query' }

    ie. User.findOneAndUpdate( { email: '[email protected]' }, { email: '[email protected]' }, { runValidators: true, context: 'query' }, function(err) { // ... }

  4. additional options

    1. case insensitive

    use the uniqueCaseInsensitive option in your schema

    ie. email: { type: String, index: true, unique: true, required: true, uniqueCaseInsensitive: true }

    1. custom error messages

    ie. exampleSchema.plugin(uniqueValidator, { message: 'Error, expected {PATH} to be unique.' });

Now you can add/delete the unique property to your schemas without worrying about restarting mongo, dropping databases, or creating indexes.

Caveats (from the docs):

Because we rely on async operations to verify whether a document exists in the database, it's possible for two queries to execute at the same time, both get 0 back, and then both insert into MongoDB.

Outside of automatically locking the collection or forcing a single connection, there's no real solution.

For most of our users this won't be a problem, but is an edge case to be aware of.

Solution 12 - Mongodb

Newest answer: there is no need to restart mongodb at all, if colleciton has same name indexes already, mongoose will not recreate your indexes again, so, drop colleciton's existing indexes firstly, and now, when you run mongoose, it will create new index, above process solved my problem.

Solution 13 - Mongodb

Restarting and using plugins didn't work for me plus it a little overkill to use plugins for something we're all sure mongo could do on it's own.

So here's the fix. in your connect function add this to the options object(2nd param)

const options = {
  autoIndex: true, //this is the code I added that solved it all
}
mongoose.connect(process.env.MONGO_URI, options);

Solution 14 - Mongodb

You can also resolve this issue by dropping the index;

let's assume you want to remove the unique index from collection users and field username, type this:

Solution 15 - Mongodb

If the table/collection is empty, then create unique index for the field:

db.<collection_name>.createIndex({'field':1}, {unique: true})

If the table/collection is not empty, then drop the collection and create index:

db.<collection_name>.drop()
db.<collection_name>.createIndex({'field':1}, {unique: true})

Now restart mongoDB.

Solution 16 - Mongodb

check that autoIndex in the schema is true, it maybe set false(default true) when you use mongoose.connect options

Solution 17 - Mongodb

I faced the same issue for awhile and did a lot of searching and the solution for me was the createIndexes() function.

I wish that helps.

so the code will be like that.

User = new Schema ({
   email: {type: String, unique: true}
});
User.createIndexes();

Solution 18 - Mongodb

an old question, but for anyone still having this issue, you probably are not applying indexes properly:

if you have autoIndex in connection options set to false then one option would be to make it a true or remove this property altogether which would revert it to its default which is true, HOWEVER, this is not recommended in production as it would cause a hit to performance, the better approach would be to explicitly call createIndexes on your model, which would properly create the indices as defined in your schema.

so the syntax for the example in the original question can be as follows:

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, index: true, unique: true },
  // other fields
});

// methods, statics, hooks... etc

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

User.createIndexes();

module.exports = User;

Solution 19 - Mongodb

If you are using the option autoIndex: false in the connection method like this:

mongoose.connect(CONNECTION_STRING, { autoIndex: false });

Try removing that. If that doesn't work, try restarting mongodb as many suggested in this thread.

Solution 20 - Mongodb

You can define your schema as

User = new Schema ({
  email: {
      type: String,
      unique: true
  }
})

But maybe this will not work when there is already a document and after that, you have changed the schema of the User. You can create an index on email for this User collection if you don't want to drop the collection. Using this command you can create an index on email.

db.User.createIndex({email:1},{unique: true})

Or you can just drop the collection and add the user again.
For Dropping the collection, you can enter this:

db.User.drop()

Solution 21 - Mongodb

When I encountered this problem, I tried dropping the database, re-starting the server (nodemon) many times and none of the tricks didn't work at all. I found the following work around, through Robo 3T:

  1. In the Robo 3T, double click on the Database to open the collections

  2. Open the collections to reveal the Collection in question. Make sure your collections are empty, to begin with

  3. Right-click on the Indexes Folder. By default, you will see the _id_ as the default. Now, choose Add Index

  4. Choose a name, say email for e-mail field, for example

  5. Provide Keys as JSON. For example

    {
       "email": 1
    }
    
  6. Click on the Unique checkbox

  7. Save

This will make sure no duplicate emails are saved in the MongoDB.

Solution 22 - Mongodb

Make sure your collection has no redundant of the field you just put the unique index on.

Then just restart your app (mongoose). It just silently add index fails.

Solution 23 - Mongodb

Removing all documents from the collection:

db.users.remove({})

And a restart, as others mentioned, worked for me

Solution 24 - Mongodb

If your MongoDB is working as a service (easy way of finding this is if you do not need to connect to the database without starting the mongod.exe file via terminal), then after doing the changes you might need to restart the service and/or drop your database fully.

It is quite strange because for some users just dropping a single collection worked. Some of them just needed to drop the database. But those did not worked for me. I dropped the database, then restarted the MongoDB Server service.

To restart a service search Services on the Windows search bar then find the MongoDB service, double click to open then stop and start the service again.

If the other methods did not work for you, I believe this will do the job.

Solution 25 - Mongodb

In my case mongoose was outdated. i checked it by running npm outdated on CMD. and updated 'mongoose'.

Please tell if that worked for you as well.

Solution 26 - Mongodb

When you connect your database with the application add this option: "audoIndex: true" for example in my code I did this:

const options = {
// your options go here
...
// this code is the solution
audoIndex: true
}
mongoose.connect(DB_URI, options);

I also dropped the collection that I have problem with and recreated it to make sure that it will work. I found this solution at: https://dev.to/emmysteven/solved-mongoose-unique-index-not-working-45d5 I also tried solutions like "restart MongoDB" but didn't work for me.

Solution 27 - Mongodb

In my case we need define schema.index to create our indexes. Please check the Mongoose documentations Indexes https://mongoosejs.com/docs/guide.html#indexes,

  • Note, after make changes in your schema, remember the restart the server to check.

Now take a look in code below to test:

const schemaUser = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true,
      index: true,
      unique: true,
      dropDups: true,
    },
    hash: String,
    created: {
      type: Date,
      default: Date.now,
    },
  },
  {
    autoCreate: true, // auto create collection
    autoIndex: true, // auto create indexes
  }
)
// define indexes to be create
schemaUser.index({ username: 1 })

const User = mongoose.model('Users', schemaUser)
const newUser = new Users({ username: 'wintzer' })
newUser.save(function (err) {
  if (err) console.log(err)
})

Solution 28 - Mongodb

in your connect function do not forget to mention useCreateIndex: true

mongoose.connect(url, {
  useNewUrlParser: true,
  useCreateIndex: true,
  useFindAndModify: false,
  useUnifiedTopology: true,
})

Solution 29 - Mongodb

You don't really need to use 'unique: true' or the mongoose-unique-validator plugin, you can simply use a custom async validate() in your type definition that uses countDocuments():

Inside your Schema... (assuming your Schema is for a User model)

email: {type: String, required: true, trim: true, lowercase: true, async validate(value) {
	const count = await	mongoose.models.User.countDocuments({email: value});
	if (count > 0) {
		const existing = await	mongoose.models.User.findOne({email: value});
		if (!(existing._id.toString() === this._id.toString())) {
			throw new Error("Email not unique");
		}
	}
}}

Solution 30 - Mongodb

If you wouldn't have specified to auto index the data which means to check for uniqueness, mongoose wouldn't do that

Simply make them to true while connecting to the database

mongoose.connect('connection url', {
    useUnifiedTopology: true,
    useNewUrlParser: true,
    useCreateIndex: true, //make this true
    autoIndex: true, //make this also true
})
.then(() => {
    console.log('Connected to mongoDB');
});

Solution 31 - Mongodb

For anyone still facing this issue, the above answers didn't solve my problem. What did solve it was going to mongodb compass, then open the collection, then go to indexes tab and click the create an index button, then in the options tick the create unique index checkbox, then create that index.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfoobarView Question on Stackoverflow
Solution 1 - MongodbfoobarView Answer on Stackoverflow
Solution 2 - MongodbjpilloraView Answer on Stackoverflow
Solution 3 - MongodbNeil StrainView Answer on Stackoverflow
Solution 4 - Mongodbsolstice333View Answer on Stackoverflow
Solution 5 - MongodbPierre MaouiView Answer on Stackoverflow
Solution 6 - Mongodbwoj.sierakView Answer on Stackoverflow
Solution 7 - MongodbManohar Reddy PoreddyView Answer on Stackoverflow
Solution 8 - MongodbDamien RomitoView Answer on Stackoverflow
Solution 9 - MongodbmoeabdolView Answer on Stackoverflow
Solution 10 - MongodbDerek HillView Answer on Stackoverflow
Solution 11 - MongodbIsaac PakView Answer on Stackoverflow
Solution 12 - Mongodbchen JackyView Answer on Stackoverflow
Solution 13 - MongodbpiusView Answer on Stackoverflow
Solution 14 - MongodbAdemola AdegbuyiView Answer on Stackoverflow
Solution 15 - MongodbRakin AfserView Answer on Stackoverflow
Solution 16 - MongodbLee QuinzeView Answer on Stackoverflow
Solution 17 - MongodbSamir HosnyView Answer on Stackoverflow
Solution 18 - MongodbWaddahView Answer on Stackoverflow
Solution 19 - MongodbRafael MejíaView Answer on Stackoverflow
Solution 20 - MongodbPulkit AggarwalView Answer on Stackoverflow
Solution 21 - MongodbBalepurView Answer on Stackoverflow
Solution 22 - MongodbZero0HoView Answer on Stackoverflow
Solution 23 - MongodbStefanBobView Answer on Stackoverflow
Solution 24 - MongodbnickView Answer on Stackoverflow
Solution 25 - Mongodbnishant srivastavaView Answer on Stackoverflow
Solution 26 - MongodbHasanView Answer on Stackoverflow
Solution 27 - MongodbHenrique Van KlaverenView Answer on Stackoverflow
Solution 28 - MongodbHicham MounadiView Answer on Stackoverflow
Solution 29 - Mongodbgimbeljw333View Answer on Stackoverflow
Solution 30 - MongodbAlaeddineView Answer on Stackoverflow
Solution 31 - MongodbAbdi mussaView Answer on Stackoverflow