MongoDB:数组元素属性的唯一索引

问题描述 投票:22回答:4

我有一个类似于这样的结构:

class Cat {
  int id;
  List<Kitten> kittens;
}

class Kitten {
  int id;
}

我想阻止用户使用相同ID创建一只有多只小猫的猫。我已经尝试创建索引如下:

db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})

但是当我尝试插入格式错误的猫时,Mongo会接受它。

我错过了什么吗?这甚至可以做到吗?

mongodb
4个回答
29
投票

据我所知,唯一索引仅强制执行不同文档的唯一性,因此会产生重复的键错误:

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级别强制执行您需要的约束,也许这是您在插入更新时可以在应用程序逻辑中检查的内容?


25
投票

确保数组字段中各个值的唯一性

除了上面的示例之外,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}})

不会有任何影响。

因此,长话短说,唯一约束不会验证数组字段的值项中的唯一性,只是两个文档在索引字段中不能具有相同的值。


3
投票

在数组属性中有一个与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"
}

0
投票

在这种情况下,您可以编写自定义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,因为它更容易指定错误。

© www.soinside.com 2019 - 2024. All rights reserved.