当先锋百科网

首页 1 2 3 4 5 6 7

本文将会介绍 MongoDB 唯一索引,它可以用于确保文档字段值的唯一性。

唯一索引

很多时候我们需要确保文档中某个字段值的唯一性,例如 email 或者 username。唯一索引(unique index)可以帮助我们实现这种业务规则。实际上,MongoDB 使用唯一索引确保主键 _id 的唯一性。

创建唯一索引的方法和普通索引相同,只需要额外指定 {unique: true} 选项:

索引示例

首先,创建一个新的集合 users,插入一些文档:

db.users.insertMany([
   { name: "张三", dob: "1990-01-01", email: "[email protected]"},
   { name: "李四", dob: "1992-06-30", email: "[email protected]"}
]);

然后,我们基于 email 字段创建一个唯一索引:

接下来我们尝试插入一个已经存在的文档:

db.users.insertOne(
    { name: "张叁", dob: "1995-03-12", email: "[email protected]"}
);

此时,MongoDB 将会返回以下错误:

下面我们演示一下集合已经存在重复数据时如何创建唯一索引。首先删除并重建集合 users:

db.users.drop()

db.users.insertMany([
   { name: "张三", dob: "1990-01-01", email: "[email protected]"},
   { name: "张叁", dob: "1995-03-12", email: "[email protected]"},
   { name: "李四", dob: "1992-06-30", email: "[email protected]"}
]);

然后,基于 email 字段创建一个唯一索引:

db.users.createIndex({email: 1},{unique:true})

MongoServerError: Index build failed: 95f78956-d5d0-4882-bfe0-2d856df18c61: Collection book.users ( 6da472db-2884-4608-98b6-95a003b4f29c ) :: caused by :: E11000 duplicate key error collection: mflix.users index: email_1 dup key: { email: zhangsan@test.com }

以上错误的原因在于 email 中存在重复的记录。

通常,我们会在插入数据之前创建唯一索引,可以从头开始确保数据的唯一性。如果基于已有数据创建唯一索引,可能会由于重复数据导致索引创建失败。为此,我们需要删除重复的数据之后再创建索引。

例如,我们可以首先删除重复的 user:

db.users.deleteOne({ name: "张叁", dob: "1995-03-12", email: "[email protected]"});

{ acknowledged: true, deletedCount: 1 }

然后基于 email 字段创建唯一索引:

db.users.createIndex({email: 1},{unique:true})

email_1

复合唯一索引

基于多个字段创建的唯一索引就是复合唯一索引(unique compound index)。复合唯一索引可以确保多个字段值的组合唯一。例如,基于字段 field1 和 field2 创建复合唯一索引,以下数据具有唯一性:

field1field2组合
11(1,1)
12(1,2)
21(2,1)
22(2,2)

以下数据存在重复值:

field1field2组合
11(1,1)
11(1,1) 重复
21(2,1)
21(2,1) 重复

接下来我们看一个示例,首先创建一个集合 locations。

db.locations.insertOne({
	address: "北京市丰台区莲花池东路118号北京西站",
	latitude: 39.894793,
	longitude: 116.321592
})

然后,基于 latitude 和 longitude 创建一个复合唯一索引:

db.locations.createIndex({
	latitude: 1,
	longitude: 1
},{ unique: true });

'latitude_1_longitude_1'

插入一个经度相同的地点:

db.locations.insertOne({
	address: "北京市海淀区复兴路甲9号中华世纪坛",
	lat: 39.910577,
	long: 116.321592
})

操作执行成功,因为复合索引 latitude_1_longitude_1 校验的是经度和纬度的组合值。

最后,插入一个经纬度已经存在的地点:

db.locations.insertOne({
	address: "北京市丰台区莲花池东路118号北京西站",
	latitude: 39.894793,
	longitude: 116.321592
})

此时,MongoDB 返回重复键错误: