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