Abhijit Sarkar
Abhijit Sarkar

Reputation: 24618

Why is Casbah/MongoDB id index Not Unique?

I'm surprised to see that the _id is not a unique index. I'm providing values for the _id field and MongoDB is creating an index alright but it's not unique. I tried to update it (by creating a new unique index on the _id field) but nothing changed. I didn't get any error either. Why is this happening and how can I make a unique index on _id?

MongoDB version (as given by version()) 3.0.6, Casbah version 2.8.2, Scala version 2.11.7.

My document structure:

{_id=1, firstName=John, lastName=Doe, phoneNum=111-111-1111, active=true, [email protected]}

Indices as dumped in the logs (timestamp etc. omitted for brevity). I'm not sure why each index is printing twice but that's an issue for another question. For the records, this is how I'm printing the indices: collection.indexInfo.foreach { index => logger.debug(s"Index: ${index.toMap}") }

Index: {v=1, key={ "_id" : 1}, name=_id_, ns=akka.users}
Index: {v=1, unique=true, key={ "phoneNum" : 1}, name=phoneNum_1, ns=akka.users}
Index: {v=1, unique=true, key={ "email" : 1}, name=email_1, ns=akka.users, sparse=true}
Index: {v=1, key={ "_id" : 1}, name=_id_, ns=akka.users}
Index: {v=1, unique=true, key={ "phoneNum" : 1}, name=phoneNum_1, ns=akka.users}
Index: {v=1, unique=true, key={ "email" : 1}, name=email_1, ns=akka.users, sparse=true}

Upvotes: 0

Views: 1103

Answers (2)

Abhijit Sarkar
Abhijit Sarkar

Reputation: 24618

A bulb went off in my head when I saw @codename44 insert statement above. Turned out it is not only enough to have a unique _id field in the document for Mongo to ensure uniqueness, that field must also be of type ObjectId. My problem was that I was inserting the field as a String.

That said, I now have the problem that none of the inserts work. There're no errors, no index violations but the document is simply not inserted. See my updated code below that always enters the 2nd case (writeResult.getN is 0):

Edit: Turned out that the failed insert was not actually failing. According to this SO post, the Java driver always returns 0 for number of rows inserted. So unless there is an exception, it is always assumed that the insert is successful.

override def createUser(user: User) = {
  val dbObj = userToDbObj(user)

  val result = Try(collection.insert(dbObj, WriteConcern.Safe))

  val newUser = user.copy(userId = Some(dbObj.get(USER_ID).toString))

  result match {
    case Success(writeResult) if (writeResult.getN == 1) => Some(newUser)
    case Success(writeResult) =>
      logger.error(s"Failed to create user: ${newUser}, write result: ${writeResult}."); None
    case Failure(ex) => logger.error("Failed to create user.", ex); None
  }
}

private def userToDbObj(user: User) = {
  val builder = MongoDBObject.newBuilder

  builder += USER_ID -> (user.userId match {
    case Some(userId) if (ObjectId.isValid(userId)) => logger.info("Using given user id."); new ObjectId(userId)
    case _ => logger.info("Generating new user id."); new ObjectId()
  })

  builder += (FIRST_NAME.toString -> user.firstName,
    LAST_NAME.toString -> user.lastName,
    PHONE_NUM.toString -> user.phoneNum)

  user.email match {
    case Some(email) => builder += EMAIL.toString -> user.email.get
    case _ =>
  }

  builder.result
}

Upvotes: 0

codename44
codename44

Reputation: 887

In a mongo courses video, it is stated that "_id" index is not tagged as unique using command db.collection.getIndexes() even if it is unique. I can't find this information in official documentation thought.

If you want to be sure, try to add another document with an existing _id field.

My _id index not tagged unique :

> db.products.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "test.products"
        }
]

Add existing index and get duplicat key error :

> db.products.insert({ "_id" : ObjectId("55ed6ccc20a18b075ba683b2")})
WriteResult({
        "nInserted" : 0,
        "writeError" : {
                "code" : 11000,
                "errmsg" : "E11000 duplicate key error index: test.products.$_id_ dup key: { : ObjectId('55ed6ccc20a18b0
75ba683b2') }"
        }
})

Upvotes: 1

Related Questions