MongoDB:数组元素上的唯一索引';s属性
我的结构与此类似:MongoDB:数组元素上的唯一索引';s属性,mongodb,Mongodb,我的结构与此类似: class Cat { int id; List<Kitten> kittens; } class Kitten { int id; } 但当我尝试插入一个格式不正确的cat时,Mongo接受了它 我错过什么了吗?甚至可以这样做吗?据我所知,唯一索引仅在不同文档之间强制唯一性,因此这会引发重复的键错误: db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } ) db.cats.insert( {
class Cat {
int id;
List<Kitten> kittens;
}
class Kitten {
int id;
}
但当我尝试插入一个格式不正确的cat时,Mongo接受了它
我错过什么了吗?甚至可以这样做吗?据我所知,唯一索引仅在不同文档之间强制唯一性,因此这会引发重复的键错误:
db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
但这是允许的:
db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )
我不确定是否有任何方法可以在Mongo级别强制执行您所需的约束,也许您可以在插入更新时在应用程序逻辑中进行检查?确保数组字段中单个值的唯一性
除了上面的示例之外,MongoDB中还有一个函数,用于确保在向数组字段添加新对象/值时,它仅在值/对象不存在时执行更新
db.cats.update(
{ id: 123 },
{ $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
{ upsert: true}
)
因此,如果您有一个如下所示的文档:
{ _id: 123, kittens: [456] }
这是允许的:
db.cats.update({_id:123}, {$push: {kittens:456}})
导致
{ _id: 123, kittens: [456, 456] }
但是,使用$addToSet功能(与$push相反)将在添加之前检查该值是否已存在。
因此,首先:
{ _id: 123, kittens: [456] }
然后执行:
db.cats.update({_id:123}, {$addToSet: {kittens:456}})
不会有任何效果
因此,长话短说,唯一约束不会验证数组字段的值项中的唯一性,只是两个文档在索引字段中不能有相同的值。数组属性中有一个等同于插入的唯一性。下面的命令本质上是插入,同时确保小猫的唯一性(如果带有123的对象不存在,upsert将为您创建它) 对象的结果值将为
{
"id": 123,
"kittens": [456],
"otherfields": "extraval",
"field2": "value2"
}
在这种情况下,您可以编写自定义Mongoose验证方法。您可以连接到后期验证。Mongoose有验证,您可以在验证之前(之前)或之后(之后)实现钩子。在这种情况下,可以使用后期验证来查看数组是否有效。然后确保阵列没有重复。根据您的详细信息,您可能会提高效率。例如,如果您只有'\u id',则可以使用JS includes函数
catSchema.post('validate',function(next) {
return new Promise((resolve,reject) => {
for(var i = 0; i < this.kittens.length; i++) {
let kitten = this.kittens[i];
for(var p = 0; p < this.kittens.length; p++) {
if (p == i) {
continue;
}
if (kitten._id == this.kittens[p]._id) {
return reject('Duplicate Kitten Ids not allowed');
}
}
}
return resolve();
});
});
catSchema.post('validate',函数(下一步){
返回新承诺((解决、拒绝)=>{
for(var i=0;i
我喜欢在验证中使用承诺,因为它更容易指定错误。使用 下面是一个示例验证器,其中“a”是一个数组,“a”子文档字段中的“b”值必须是唯一的。这假定集合为空或已符合以下规则:
>db.runCommand({collMod:“coll”,验证器:{$expr:{$eq:[{$size:$a.b”},{$size:{$setUnion:$a.b”}]})
/*测试一下*/
>db.coll.insert({a:[{b:1}]})/*成功*/
>db.coll.update({},{$push':{'a':{b:1}})
书面结果({
“nMatched”:0,
“nUpserted”:0,
“nModified”:0,
“writeError”:{
“代码”:121,
“errmsg”:“文档验证失败”
}
})
这里似乎很重要的一点是确保mongodb对象数组中只存在一个项,并且具有相同的id或其他一些需要唯一处理的字段。然后,使用$addToSet
,这样一个简单的查询就足以进行更新
请原谅,我不是一个使用Java mongo驱动程序版本4.0.3的mongo shell专家
collection = database.getCollection("cat", Cat.class);
UpdateResult result = collection.updateOne(and(eq("Id", 1), nin("kittens.id", newKittenId)), addToSet("kittens", new Kitten("newKittenId")));
这里使用的查询为匹配查询添加了一个额外的条件,如下所示:;其中cat.id为1,并且newKittenId尚未归先前添加的任何小猫所有。因此,如果找到了猫的id,并且没有小猫使用了新的小猫id,那么查询将继续进行,并通过添加新的小猫来更新猫的小猫。但是,如果newKittenId被其中一只小猫拿走,它只返回updateresult,不带任何计数,也不带任何修改的字段(不会发生任何事情)
注意:这并不能确保kitten.id上的唯一约束,mongo DB不支持文档中对象数组的唯一性,addToSet也不真正处理对象数组中的重复项,除了对象是数据库中内容的100%副本之外,请检查关于
addToSet
的内容。请看,这不是关于一只猫不接受同一只猫两次。你不能让两只猫和同一只小猫在一起。kittens.id必须在所有猫中都是唯一的,这就是为什么小猫不能同时有妈妈和爸爸。不幸的是,你似乎是对的,我必须在代码中强制执行这一点。哦,好吧,太伤心了(.应该有一个选项来确保文档数组中的唯一性。您可以做的是在数组中保存时编写验证挂钩,以确保它是唯一的unique@Chris我正试图通过标记实现同样的目标:[{type:Schema.Types.ObjectId,ref:“Tag”,}]
但不断出现错误,如MongoError:E11000重复密钥错误集合:kh-content-server.posts索引:tags_1 dup key:{tags:null}
重要属性是:$addToSet
和upsert
。$set
属性与OP无关。我们有任何原始mongodb方法,而不是mongoose验证器吗
collection = database.getCollection("cat", Cat.class);
UpdateResult result = collection.updateOne(and(eq("Id", 1), nin("kittens.id", newKittenId)), addToSet("kittens", new Kitten("newKittenId")));