4

I have declared a unique index on a schema like so:

var stateChangeEventSchema = mongoose.Schema({
  client_timestamp: { type: Date, required: true, unique: true },
  state: { type: String, required: true }
})

And am able to insert a document with identical timestamps. I would expect a constraint violation. I thought it might be because it's on an embedded document, but I tried on a regular document, and it still allows it. What am I missing?

Edit: I found the issue. I was failing to create another index, which for whatever reason prevented any other indexes from being created.

I found this by using:

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

which netted: err: 'Index with name: _id_ already exists with different options'

This occurred because I had the following line: _id: { type: String, unique: true, 'default': shortId.generate } (edited out in the original code for clarity - didn't think it would affect anything).

Removing that line allowed the client_timestamp index to be created.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
Dominic Bou-Samra
  • 14,799
  • 26
  • 100
  • 156

1 Answers1

4

Looks like you already had duplicate values before you made this change.

Here is a test case. Have two documents in your collection like this before you deploy an index:

{ "timestamp" : ISODate("2014-06-02T04:09:22.683Z") }
{ "timestamp" : ISODate("2014-06-02T04:09:22.683Z") }

Then with the basic listing:

    var mongoose = require('mongoose'),
        Schema = mongoose.Schema;

    mongoose.connect('mongodb://localhost/test');

    var stateChange = mongoose.Schema({
      timestamp: { type: Date, required: true, unique: true }
    });

    var Change = mongoose.model( 'Change', stateChange );

    var date = new Date();

    var change = new Change({
      timestamp: date
    });

    change.save(function(err,change) {

      if ( err )
        throw err;

      console.log( change );

      var new_change = new Change({
        timestamp: date
      });

      new_change.save(function(err,change) {

        if ( err )
          throw err;

        console.log( change );

      });

    });

You'll see both documents are added. But the thing here is that the index did not deploy because that caused an error you did not see. You can check this in the shell.

db.changes.getIndicies()

Which will show your unique index was not created:

[
    {
            "v" : 1,
            "key" : {
                    "_id" : 1
            },
            "name" : "_id_",
            "ns" : "test.changes"
    }
]

If you started again and only had one document from the original

{ "timestamp" : ISODate("2014-06-02T04:09:22.683Z") }

Then the code sample above creates the index and produces an error in the second insert:

{ __v: 0,
  timestamp: Mon Jun 02 2014 14:29:44 GMT+1000 (EST),
  _id: 538bfdb8961376867ae42e61 }

/xxxxx/node_modules/mongoose/lib/utils.js:413
    throw err;
          ^
MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.changes.$timestamp_1  dup key: { : new Date(1401683384647) }

The index created properly this time:

[
    {
            "v" : 1,
            "key" : {
                    "_id" : 1
            },
            "name" : "_id_",
            "ns" : "test.changes"
    },
    {
            "v" : 1,
            "unique" : true,
            "key" : {
                    "timestamp" : 1
            },
            "name" : "timestamp_1",
            "ns" : "test.changes",
            "background" : true,
            "safe" : null
    }
]

You are going to need to go through your data to remove the existing duplicates or just accept the usage of the dropDups option to automatically remove them for you.

Also see the documentation tutorial: Create A Unique Index

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317