MongoJS - No Error On Unique Index

247 views Asked by At

So I'm messing around with MongoDB using MongoJS and can't seem to get an error to be returned when a unique ID exists.

So this is the code I am using:

server.post('/register', function (req, res, next) {
  var details = req.params;

  details.password = md5(details.password).toString();

  db.users.insert(details,
    function (err, test1, test2) {
      console.log(err);
  });

});

So I hit /register with username=username&password=password and initially it will add it to the database. If I then visit the same URL again, I don't get any errors that there is already an entry with that username even though I have the following set in MongoLab:

{
  "v": 1,
  "unique": true,
  "key": {
    "username": 1
  },
  "name": "username",
  "ns": "restapi.users",
  "background": true
}

What do I need to do to get the 'err' to return the correct error code for duplicate entry? I believe it should return 11000.

Thanks

1

There are 1 answers

0
AudioBubble On BEST ANSWER

This seems to be a "buggy" response from mongojs which likely results from it's implementation of returning the "inserted" object as a response. The "core" driver does not do this but would either return a "ok" response or an error:

  var MongoClient = require('mongodb').MongoClient;

  MongoClient.connect('mongodb://localhost/test', function(err,db) {

    var collection = db.collection('users');

    collection.insert({ "username": 2 },function(err,res) {
      if (err) throw err;
      console.log( res );
    });

  });

So it seems that mongojs "mucks this up" while attempting this change in the API behaviour. Consequently, the errors from Bulk operations are also not properly reported. But at least you can get a response that tells you for sure that nothing is actually inserted:

var mongojs = require('mongojs'),
    db = mongojs('test');


var collection = db.collection('users')

var bulk = collection.initializeOrderedBulkOp();

bulk.insert({ "username": 2 });
bulk.execute(function(err,res) {
  if (err) throw err;
  console.log( res );
});

If the username exists then the response would be:

{ writeErrors: [],
  writeConcernErrors: [],
  nInserted: 0,
  nUpserted: 0,
  nMatched: 0,
  nModified: 0,
  nRemoved: 0,
  upserted: [],
  ok: 1 }

If it's a new "unique" value then the response would be:

{ writeErrors: [],
  writeConcernErrors: [],
  nInserted: 1,
  nUpserted: 0,
  nMatched: 0,
  nModified: 0,
  nRemoved: 0,
  upserted: [],
  ok: 1 }

If you wanted the _id of a new document at least, I would have said to use "upserts" with Bulk operations, but the implementation there is broken as well. In fact the "wrapping" methods still done by monjojs do not include any option to use "upserts" at all.

So the final thing you can do is use the .runCommand() method from the db object and process the "upsert" that way:

var mongojs = require('mongojs'),
    db = mongojs('test');

db.runCommand(
  {
    "update": "users",
    "updates": [
      {
        "q": { "username": 4 },
        "u": { "$setOnInsert": { "username": 4 } },
        "upsert": true
      }
    ],
    "ordered": true
  },
  function(err,res) {
    if (err) throw err;
    console.log( res );
  }
);

It still does not report the error for the duplicate key ( upserts won't do that ) but the response at least tells you the relevant information. On "upsert" it will report this:

{ ok: 1,
  nModified: 0,
  n: 1,
  upserted: [ { index: 0, _id: 5580da465e2076d9ea0c5885 } ] }

And where the value is found and the data is therefore not modified due to $setOnInsert then the response would be:

{ ok: 1, nModified: 0, n: 1 }

So if you are going to still work with mongojs then you need to accept the limitations and work around them as demonstrated.