如何为嵌套在数组中的文档中的字段设置唯一约束?

Posted

技术标签:

【中文标题】如何为嵌套在数组中的文档中的字段设置唯一约束?【英文标题】:How to set unique constraint for field in document nested in array? 【发布时间】:2020-08-22 14:35:02 【问题描述】:

我在 MongoDB 中有一组文档,如下所示:

"_id": 1, "array": ["id": 1, "content": "...", "id": 2, "content": "..."]
"_id": 2, "array": ["id": 1, "content": "...", "id": 2, "content": "...", "a_id": 3, "content": "..."]

我想确保每个文档中没有重复的array.id。所以提供的例子是可以的,但followign不是:

"_id": 1, "array": ["id": 1, "content": "...", "id": 1, "content": "..."]

我的问题是如何做到这一点(最好在PyMongo 中)。

编辑

我尝试的是以下代码,我认为它会在 (_id, array.id) 上创建密钥,但如果你运行它,这不会发生:

from pymongo import MongoClient, ASCENDING

client = MongoClient(host="localhost", port=27017)
database = client["test_db"]
collection = database["test_collection"]
collection.drop()

collection.create_index(keys=[("_id", ASCENDING),
                              ("array.id", ASCENDING)],
                        unique=True,
                        name="new_key")

document = "array": ["id": 1, "id": 2]
collection.insert_one(document)

collection.find_one_and_update("_id": document["_id"],
                               "$push": "array": "id": 1)

updated_document = collection.find_one("_id": document["_id"])

print(updated_document)

哪个输出(注意array中有两个带有id = 1的对象)。我希望得到一个例外。

'_id': ObjectId('5eb51270d6d70fbba739e3b2'), 'array': ['id': 1, 'id': 2, 'id': 1]

【问题讨论】:

请查看此类似问题并回答Eliminate duplicates in MongoDB with a specific sort @prasad_ 在您提到的帖子中,它仅描述了如何确保您在执行查询时没有重复项,但我希望数据库检查是否有重复项,例如插入一个新的价值。基本上我想要实现的是在(_id, id) 上设置一个“索引” 插入(推入)数组是一种更新操作。确保更新的查询条件检查 id 不存在于子文档数组中。您还可以在数组字段上设置索引 - 这些被称为 Multikey Indexes。请参阅链接中的子主题:唯一多键索引 @prasad_ 我认为多键索引是我正在寻找的。我试过了,但没有运气。所以从文档中我假设我应该将索引设置为db.coll.createIndex( "array.id": 1 ) 对吗?我试过了,它不起作用。 请解释一下您所说的“我试过了,但它不起作用。”. 【参考方案1】:

因此,如果我理解正确,则无法设置索引(或 某些条件)将强制文档内的唯一性, 正确的? (除了在创建文档时明确检查或 插入时。)

是的。关于在嵌入文档的数组字段上使用唯一索引,请参见以下两种场景。

Unique Multikey Index(数组内嵌入文档字段的索引):

对于唯一索引,唯一约束适用于不同的 集合中的文档而不是单个文档中的文档。

因为唯一性约束适用于单独的文档,对于一个 唯一的多键索引,文档可能有数组元素,结果 重复索引键值,只要该索引键值 文档不要与其他文档重复。

第一个场景:

db.arrays.createIndex(  _id: 1, "array.id": 1,  unique: true  )

db.arrays.insertOne(  "_id": 1, "array": [  "id": 1, "content": "11",  "id": 2, "content": "22" ]  )

db.arrays.insertOne(  "_id": 2, "array": [  "id": 1, "content": "1100",  "id": 5, "content": "55" ]  )

db.arrays.insertOne( "_id": 3, "array": [ "id": 3, "content": "33", "id": 3, "content": "3300" ]  )

所有三个文档都被插入,没有任何错误。

根据上面唯一多键索引的注释,_id : 3 的文档在数组中有两个嵌入的文档,具有相同的"array.id"value:@987654325 @。

此外,对复合索引 _id: 1, "array.id": 1 的两个键强制执行唯一性,并且文档中也存在重复的 "array.id" 值(_id12)。

第二种情况:

db.arrays2.createIndex(  "array.id": 1 ,  unique: true  )

db.arrays2.insertOne(  "_id": 3, "array": [  "id": 3, "content": "33" ,  "id": 3, "content": "330" ]  )
db.arrays2.insertOne(  "_id": 4, "array": [  "id": 3, "content": "331" ,  "id": 30, "content": "3300"  ]  )

第一个带有_id : 3 的文档被成功插入。第二个有错误:"errmsg" : "E11000 duplicate key error collection: test.arrays2 index: array.id_1 dup key: array.id: 3.0 "。此行为与注释 Unique Multikey Index 中的预期一致。

【讨论】:

如果我们这样做,那么它在数据库级别变得独一无二,我想在文档级别进行,有两个数组字段【参考方案2】:

您可以在更新时进行此检查

const doc = await Model.findOneAndUpdate(
     _id, 'array.id':  $ne: newID ,
    
      $push: 
        array: newID
      
    ,
     new: true 
  );

【讨论】:

以上是关于如何为嵌套在数组中的文档中的字段设置唯一约束?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 MySQL 中的多个列指定唯一约束?

如何为 postgresql 中的唯一(不包括顺序)JSONB 列创建约束

如何为数组中的每个创建一个具有唯一图像和选择器的自定义 UIButton?

如果没有插入值,如何为 SQL Server 中的列字段分配唯一值?

如何为 MongoDB 中的嵌入式文档创建唯一 ID?

唯一约束