为什么 Casbah/MongoDB id 索引不唯一?

Why is Casbah/MongoDB id index Not Unique?

我很惊讶 _id 不是唯一索引。我正在为 _id 字段提供值,MongoDB 正在创建一个索引,但它不是唯一的。我试图更新它(通过在 _id 字段上创建一个新的唯一索引)但没有任何改变。我也没有收到任何错误。为什么会发生这种情况,如何在 _id 上创建唯一索引?

MongoDB版本(由version()给出)3.0.6,Casbah版本2.8.2,Scala版本2.11.7.

我的文档结构:

{_id=1, firstName=John, lastName=Doe, phoneNum=111-111-1111, active=true, email=test@gmail.com}

日志中转储的索引(为简洁起见省略了时间戳等)。我不确定为什么每个索引都打印两次,但这是另一个问题。作为记录,这就是我打印索引的方式: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}

在 mongo 课程视频中,指出“_id”索引未使用命令 db.collection.getIndexes() 标记为唯一,即使它 唯一. 我在官方文档中找不到这个信息。

如果您想确定,请尝试添加另一个具有现有 _id 字段的文档。

我的 _id 索引未标记为唯一:

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

添加现有索引并获取重复键错误:

> 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') }"
        }
})

当我看到上面的 @codename44 insert 语句时,我的脑子里突然冒出一个灯泡。事实证明,不仅 Mongo 的文档中有一个唯一的 _id 字段来确保唯一性,该字段还必须是 ObjectId 类型。我的问题是我将字段作为 String.

插入

就是说,我现在遇到的问题是 none 的插入有效。没有错误,没有违反索引,但根本没有插入文档。请参阅下面我更新的代码,该代码始终进入第二种情况(writeResult.getN 为 0):

Edit: 事实证明,失败的插入实际上并没有失败。根据 this SO post,对于插入的行数,Java 驱动程序总是 returns 0。所以除非有异常,否则总是假定插入成功。

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
}