Automattic / mongoose

MongoDB object modeling designed to work in an asynchronous environment.
https://mongoosejs.com
MIT License
26.92k stars 3.84k forks source link

How to get index name on duplicate document 11000 error? #2129

Closed niftylettuce closed 9 years ago

niftylettuce commented 10 years ago

How can I get the index for which the error occurs with?

e.g. if my index is named email, how do I get that from the err object?

vkarpov15 commented 10 years ago

The best way right now is by parsing the error message (slightly hacky, yes). The error message looks like:

E11000 duplicate key error index: test.x.$a_1 dup key: { : 1.0 }

Looks pretty consistent across mongodb server 2.4 and 2.6. The index: indicates which index causes the duplicate key error on the server side.

niftylettuce commented 10 years ago

@vkarpov15 here's how I did it, in case anyone else wants to do this, or if you would like to add it to the wiki, documentation, or Readme perhaps -- it is rather useful for error handling.

// this example is showing how to get the field `email` out of the err message 
var field = err.message.split('index: test.')[1].split('.$')[1]
// now we have `email_1 dup key`
field = field.split(' dup key')[0]
field = field.substring(0, field.lastIndexOf('_')) // returns email

I am using this directly in the error handler component of my new framework called Igloo.

https://github.com/niftylettuce/igloo

The commit with it:

https://github.com/niftylettuce/igloo/commit/3cdee8dcc28b6373a80a4e0c16c3f2c34edfca6c

paambaati commented 9 years ago

@niftylettuce Can we not get it this way?

var field = err.message.split('.$')[1].split('_')[0];

This way, one does not have to know the database name.

EDIT on 31 January, 2015:

That breaks when the field name has an underscore in it. Use this instead.

var field = err.message.split('.$')[1];
// now we have `email_1 dup key`
field = field.split(' dup key')[0];
field = field.substring(0, field.lastIndexOf('_')); // returns email
paambaati commented 9 years ago

@vkarpov15 After Mongoose 4.x, the error format seems to have changed a bit, and no longer contains the offending field name.

The error message, for example, on a field with unique:true, sparse:true and a null value looks like this -

E11000 duplicate key error dup key: { : "<whatever_value_i_tried_to_insert>" }

Is there a way to get the field that is causing this error?

vkarpov15 commented 9 years ago

@paambaati that's not a mongoose issue, that string is generated by the mongodb server and is specific to the WiredTiger storage engine in mongodb 3.0.0 and 3.0.1. This bug was reported in the core server jira project, fixed, and the fix is in mongodb 3.0.2

bookercodes commented 9 years ago

Thanks to @paambaati I got the functionality I needed.

var user = new User(req.body);
  user.save(function(error) {
    if (error) {
      if (error.code === 11000) {
        // email or username could violate the unique index. we need to find out which field it was.
        var field = error.message.split(".$")[1];
        field = field.split(" dup key")[0];
        field = field.substring(0, field.lastIndexOf("_"));
        req.flash("errors", [{
          msg: "An account with this " + field + " already exists."
        }]);
        res.redirect("/join");
        return;
      }
      throw error;
    }
    req.flash("messages", "Thank you for joining.");
    res.redirect("/");
  });

But is there a terser way to get the same result? //cc @vkarpov15

bookercodes commented 9 years ago

I wrote a module to extract the duplicate field. I hope someone finds it useful :heart:!

vkarpov15 commented 9 years ago

Good job @alexbooker! There's also another plugin for that, see #2284 and https://www.npmjs.com/package/mongoose-beautiful-unique-validation

nebulou5 commented 8 years ago

@vkarpov15 Dude, thank you! No more ugly 11000 and 11001 checks!

vkarpov15 commented 8 years ago

Thank @alexbooker and co, they wrote the plugins :)

thomas-lee commented 8 years ago

Not sure if anyone still want to extract the value.

here is my code

const msg = 'E11000 duplicate key error dup key: { : "<whatever_value_i_tried_to_insert>" }';
msg.match(/dup key: { : "(.+)" }/)[1];
// <whatever_value_i_tried_to_insert>
ghost commented 8 years ago

And here i wrote mine to get both value and key This assumes fixed error msg format

const err.message = 'E11000 duplicate key error index: dev_app.users.$username_1  dup key: { : "debjyoti1" }'
let key_val = err.message.match(/index\:\ [a-z_]+\.[a-z_]+\.\$([a-z_]+)\_[0-9a-z]{1,}\s+dup key[: {]+"(.+)"/).splice(1,3);
// [ 'username', 'debjyoti1' ]
Fire7 commented 7 years ago

Here is my RegExp solution to get index name with any type of error syntax I have found:

  var regex = /index\:\ (?:.*\.)?\$?(?:([_a-z0-9]*)(?:_\d*)|([_a-z0-9]*))\s*dup key/i,      
  match =  error.message.match(regex),  
  indexName = match[1] || match[2];  

Here what I have tested:

('E11000 duplicate key error collection: db.users index: name_1 dup key: { : "Kate" }').match(regex)[1]; // "name"
("E11000 duplicate key error index: myDb.myCollection.$id dup key: { : ObjectId('57226808ec55240c00000272') }").match(regex)[2] // "id"
('E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }').match(regex)[1] // "b"
('E11000 duplicate key error collection: upsert_bug.col index: _id_ dup key: { : 3.0 }').match(regex)[1] // "_id"
aichholzer commented 7 years ago

My two cents:

let err = 'E11000 duplicate key error collection: home.users index: name_1 dup key: { : "francis" }';
let [i, field, value] = err.match(/index:\s([a-z]+).*{\s?\:\s?"([a-z]+)"/i);

console.log(field, value);
// name francis

MongoDB: v3.4.2

nicky-lenaers commented 5 years ago

@Fire7 Thanks for your solution. I have, however, a compound unique index for say a name and an owner field on a project model, such that there is never a project created with the same name by one owner.

Using your solution gives me name_1_owner. Any idea how to solve this for compound unique indexes?

Thanks!

vkarpov15 commented 5 years ago

@nicky-lenaers try this plugin

Maxtermax commented 5 years ago

Update 2019, for any one who wanna to use the plugin mongoose-beautiful-unique-validation be aware of this issue when use mongodb 3.6 to up

arnavzek commented 5 years ago

you guys can just use err.message.indexOf( 'theFieldInQuestion_1' ) === -1