Mongodb 唯一部分索引

Mongodb抛出错误:E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }

PS重点:mongodb 版本一定要在3.2以上才支持唯一部分索引

  • 1 背景
  • 2 唯一索引和缺失字段
  • 3 解决既要保证某个key唯一又能允许缺失的方法
    • 3.1 唯一部分索引概念
    • 3.2 官方sample code
    • 3.3 实际使用
  • 4 参考链接

1 背景

最近有个需求,账户体系要同时支持手机号和邮箱。

使用mongodb存储数据时发现了一个问题:
我们肯定会要求邮箱和手机号在账号体系下均是唯一的。
即用上{ unique : true } 参数

但这就带来了一个问题,多个用户都用邮箱注册,第一个用户邮箱是可以注册成功的,因为email 唯一 且mobilenumber 缺失,mongodb会将该值存储为空值。但第二个用户注册就有抛出错误,因为email唯一,但mobilenumber同样缺失,同样是空值,这里就违反唯一索引的规则所以抛出了错误。

也就是mongodb 文档中提到了:唯一索引和缺失字段问题

2 唯一索引和缺失字段

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

3 解决既要保证某个key唯一又能允许缺失的方法

这里就可以用到:唯一部分索引

3.1 唯一部分索引概念

部分索引仅索引符合指定过滤器表达式的集合中的文档。如果同时指定 partialFilterExpression了唯一约束,
则唯一约束仅适用于满足过滤器表达式的文档。

如果文档不符合筛选条件,则具有唯一约束的部分索引不会阻止插入不符合唯一约束的文档。有关示例,请参阅 具有唯一约束的部分索引。

3.2 官方sample code

数据库中内容
{ "_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 } )

3.3 实际使用

注意:一定要查看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
        }

4 参考链接

https://docs.mongodb.com/manual/core/index-unique/#unique-partial-indexes

你可能感兴趣的:(数据库)