MongoDB:数组元素属性的唯一索引
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MongoDB:数组元素属性的唯一索引相关的知识,希望对你有一定的参考价值。
我有一个类似于这样的结构:
class Cat {
int id;
List<Kitten> kittens;
}
class Kitten {
int id;
}
我想阻止用户使用相同ID创建一只有多只小猫的猫。我已经尝试创建索引如下:
db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})
但是当我尝试插入格式错误的猫时,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中还有一个函数可以确保在向数组字段添加新对象/值时,如果值/对象尚不存在,它将仅执行更新。
因此,如果您有一个如下所示的文档:
{ _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}})
不会有任何影响。
因此,长话短说,唯一约束不会验证数组字段的值项中的唯一性,只是两个文档在索引字段中不能具有相同的值。
在数组属性中有一个与uniquness相同的插入。以下命令基本上插入,同时确保小猫的唯一性(如果123的对象尚不存在,则upsert为您创建)。
db.cats.update(
{ id: 123 },
{ $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
{ upsert: true}
)
对象的结果值将是
{
"id": 123,
"kittens": [456],
"otherfields": "extraval",
"field2": "value2"
}
在这种情况下,您可以编写自定义Mongoose验证方法。你可以挂钩验证。 Mongoose有验证,您可以在验证之前(之前)或之后(之后)实现一个钩子。在这种情况下,您可以使用后验证来查看数组是否有效。然后确保阵列没有重复。根据您的详细信息,您可以提高效率。例如,如果你只有'_id',你可以使用JS include函数。
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();
});
});
我喜欢在验证中使用promises,因为它更容易指定错误。
以上是关于MongoDB:数组元素属性的唯一索引的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB——索引属性之唯一索引(Unique Indexes)