【问题标题】:E11000 (DuplicateKey) error when using a partial multikey unique index使用部分多键唯一索引时出现 E11000 (DuplicateKey) 错误
【发布时间】:2020-11-06 06:47:45
【问题描述】:

考虑一个包含以下文档的集合:

{ 
  name: "John Doe",
  emails: [
    { 
      value: "some@domain.com", 
      isValid: true, 
      isPreferred: true 
    }
  ]
},
{ 
  name: "John Doe",
  emails: [ 
    { 
       value: "john.doe@gmail.com",
       isValid: false,
       isPreferred: false
    },
    { 
       value: "john.doe@domain.com",
       isValid: true,
       isPreferred: true
    } 
  ]
}

应该没有用户拥有相同的有效和首选电子邮件,因此有一个唯一索引:

db.users.createIndex( { "emails.value": 1 }, { name: "loginEmail", unique: true, partialFilterExpression: { "emails.isValid": true, "emails.isPreferred": true } } )

将以下电子邮件添加到第一个文档会触发违反唯一约束:

{
  name: "John Doe",
  emails: [
   { 
     value: "john.doe@gmail.com",
     isValid: false,
     isPreferred: false
   }
  ]
}

原因:com.mongodb.MongoCommandException:命令失败 error 11000 (DuplicateKey): 'E11000 重复键错误收集: profile.users 索引:loginEmail 复制键:{ emails.value: “john.doe@gmail.com”,emails.isValid:假,emails.isPreferred:假 }' 在服务器配置文件-db-mongodb.dev:27017 上。完整的回应是 {"ok": 0.0, "errmsg": "E11000 重复键错误收集: profile.users 索引:loginEmail 复制键:{ emails.value: “john.doe@gmail.com”,emails.isValid:假,emails.isPreferred: 假}”,“代码”:11000,“代码名称”:“DuplicateKey”,“keyPattern”: {“emails.value”:1,“emails.isValid”:1,“emails.isPreferred”:1}, “keyValue”:{“emails.value”:“john.doe@gmail.com”,“emails.isValid”: false, "emails.isPreferred": false}}

据我所知,这是因为过滤器表达式应用于集合,而不是嵌入文档,因此尽管有点违反直觉和意外,但索引的行为与描述的一样。

我的问题是如何确保部分唯一性而不会出现误报?

【问题讨论】:

    标签: database mongodb indexing nosql unique-constraint


    【解决方案1】:

    TLDR:你不能。

    让我们先了解它发生的原因,也许然后我们会了解可以做什么。问题源于两个 Mongo 功能的组合。

    1. dot notation 语法。点符号语法允许您轻松查询数组中的子文档 ("emails.isPreferred": true)。但是,当您想开始将multiple conditions 用于子文档时,例如在您的情况下,您需要使用$elemMatch 之类的东西,遗憾的是partialFilterExpression 的限制非常严格,并没有给您这样的权力。 这意味着即使是带有电子邮件的文档,例如:
    {
        "_id": ObjectId("5f106c0e823eea49427eea64"),
        "name": "John Doe",
        "emails": [
            {
                "value": "john.doe@gmail.com",
                "isValid": true,
                "isPreferred": false
            },
            {
                "value": "john.doe@domain.com",
                "isValid": false,
                "isPreferred": true
            }
        ]
    }
    

    将被索引。好吧,我们将在集合中添加一些额外的索引文档,但除了(错误地)增加索引大小之外,您仍然希望它可能会起作用,但它不是由于第 2 点。

    1. multikey indexes:

    MongoDB 使用多键索引来索引存储在数组中的内容。 ... ,MongoDB 为数组的每个元素创建单独的索引条目。

    因此,当您在数组或数组中子文档的任何字段上创建索引时,Mongo 将“展平”数组并为每个文档创建一个唯一条目。在这种情况下,它将为数组中的所有emails 创建一个唯一索引。

    因此,由于所有这些“功能”以及部分过滤器语法使用的限制,我们无法真正实现您想要的。

    那你能做什么?我相信您已经在考虑可能的解决方法。一个简单的解决方案是维护一个仅包含isValidisPreferred 电子邮件的额外字段。那么唯一的稀疏索引就可以解决问题。

    【讨论】:

      猜你喜欢
      • 2015-03-22
      • 1970-01-01
      • 1970-01-01
      • 2017-03-02
      • 2016-10-31
      • 2018-04-20
      • 2019-07-11
      • 2017-01-03
      相关资源
      最近更新 更多