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

Jos*_*rej 6 arrays indexing mongodb pymongo

我在 MongoDB 中有一个文档集合,如下所示:

{"_id": 1, "array": [{"id": 1, "content": "..."}, {"id": 2, "content": "..."}]}
{"_id": 2, "array": [{"id": 1, "content": "..."}, {"id": 2, "content": "..."}, {"a_id": 3, "content": "..."}]}
Run Code Online (Sandbox Code Playgroud)

我想确保每个文档中没有重复的内容array.id。所以提供的示例是可以的,但以下示例则不行:

{"_id": 1, "array": [{"id": 1, "content": "..."}, {"id": 1, "content": "..."}]}
Run Code Online (Sandbox Code Playgroud)

我的问题是如何做到这一点(最好是在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)
Run Code Online (Sandbox Code Playgroud)

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

{'_id': ObjectId('5eb51270d6d70fbba739e3b2'), 'array': [{'id': 1}, {'id': 2}, {'id': 1}]}
Run Code Online (Sandbox Code Playgroud)

pra*_*ad_ 2

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

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

唯一多键索引(数组中嵌入文档字段的索引):

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

由于唯一约束适用于单独的文档,因此对于唯一的多键索引,只要该文档的索引键值不与另一个文档的索引键值重复,文档就可能具有导致重复索引键值的数组元素。


第一个场景:

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"} ] } )
Run Code Online (Sandbox Code Playgroud)

所有三个文档均已插入,没有任何错误。

根据上面有关“唯一多键索引”的注释,该文档_id : 3在数组中有两个具有相同"array.id"值的嵌入文档:3

此外,复合索引的两个键强制执行唯一性,并且文档中也{ _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" } ] } )
Run Code Online (Sandbox Code Playgroud)

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