Mongodb抛出错误:E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }
PS重点:mongodb 版本一定要在3.2以上才支持唯一部分索引
最近有个需求,账户体系要同时支持手机号和邮箱。
使用mongodb存储数据时发现了一个问题:
我们肯定会要求邮箱和手机号在账号体系下均是唯一的。
即用上{ unique : true } 参数
但这就带来了一个问题,多个用户都用邮箱注册,第一个用户邮箱是可以注册成功的,因为email 唯一 且mobilenumber 缺失,mongodb会将该值存储为空值。但第二个用户注册就有抛出错误,因为email唯一,但mobilenumber同样缺失,同样是空值,这里就违反唯一索引的规则所以抛出了错误。
也就是mongodb 文档中提到了:唯一索引和缺失字段问题
Mongodb 遇到这种情况的行为是:
如果文档没有唯一索引中索引字段的值,则索引将为此文档存储空值。由于唯一约束,MongoDB只允许一个缺少索引字段的文档。如果有多个文档没有索引字段的值或缺少索引字段,则索引构建将失败并出现重复键错误。
即下面这种操作就会出现这种现象:
db.collection.createIndex( { "x": 1 }, { unique: true } )
db.collection.insert( { y: 1 } )
db.collection.insert( { z: 1 } )
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }"
}
})
这里就可以用到:唯一部分索引
部分索引仅索引符合指定过滤器表达式的集合中的文档。如果同时指定 partialFilterExpression了唯一约束,
则唯一约束仅适用于满足过滤器表达式的文档。
如果文档不符合筛选条件,则具有唯一约束的部分索引不会阻止插入不符合唯一约束的文档。有关示例,请参阅 具有唯一约束的部分索引。
数据库中内容
{ "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 }
{ "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 }
{ "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }
创建唯一部分索引 条件为age大于21
db.users.createIndex(
{ username: 1 },
{ unique: true, partialFilterExpression: { age: { $gte: 21 } } }
)
下列插入将被拒绝
db.users.insert( { username: "david", age: 27 } )
db.users.insert( { username: "amanda", age: 25 } )
db.users.insert( { username: "rajiv", age: 32 } )
下列插入将被允许
db.users.insert( { username: "david", age: 20 } )
db.users.insert( { username: "amanda" } )
db.users.insert( { username: "rajiv", age: null } )
注意:一定要查看mongodb版本,mongod –version,大于3.2才支持。
为了解决moblienumber注册时,带来的email为空带来对唯一索引的问题。
按照官方文档,应该这样创建索引
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { email: {$ne:null} } }
)
但实际使用中发现部分索引不支持$ne
表达式。
所以我们尝试$exists
操作符。
所以对email字段不存在的情况下进行测试,发现下面两条语句效果一致:
db.users.find({email:{$eq:null}})
db.users.find({email:{$exists:false}})
所以我们创建唯一部分索引为
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { email: {$exists: true}} }
)
使用db.users.getIndexes()语句可以看到其中一个唯一部分索引为:
{
"v" : 2,
"unique" : true,
"key" : {
"mobilenumber" : 1
},
"name" : "mobilenumber_1",
"ns" : "jwli.users",
"partialFilterExpression" : {
"mobilenumber" : {
"$exists" : true
}
},
"background" : true
}
https://docs.mongodb.com/manual/core/index-unique/#unique-partial-indexes