在一个集合的所有文档上具有唯一值的数组

时间:2017-03-23 16:52:58

标签: mongodb mongoose

情况: 我有几个相同集合(帐户)的文档,每个文档都有一个类型为array(string)的属性,名为uniquestrings。

问题: uniquestrings中的每个条目必须对mongodb中的所有文档都是唯一的。似乎MongoDB / Mongoose没有提供这样的验证(addToSet¹和index:{unique:true}²都没有解决问题)。是否有一种模式来重构我的文档架构以确保mongodb本身可以验证它?目前,软件本身在更新文档之前对其进行检查。

E.g。

account {
  _id: 111,
  uniquestrings: ["a", "b", "c"]
}

account {
  _id: 222,
  uniquestrings: ["d", "e", "f"]
}

E.g。通过从mongo中抛出重复错误来阻止account(222).uniquestrings.push("a");

¹阵列中的唯一性是不够的 ²数组中的每个项目必须在整个集合中是唯一的

UPDATE1:

更多例子。受影响的架构条目如下所示:

var Account = new Schema({
    ...
    uniquestrings: [{type: String, index: {unique: true}}]
    ...
});

现在创建4个帐户文档。我想只有1和2可以,休息应该失败。

var accountModel1 = new Account.Model({uniquestrings: ["a", "b"]});
accountModel1.save(); // OK
var accountModel2 = new Account.Model({uniquestrings: ["c", "d"]});
accountModel2.save(); // OK
var accountModel3 = new Account.Model({uniquestrings: ["c", "d"]});
accountModel3.save(); // FAIL => Not unique, so far so good
var accountModel4 = new Account.Model({uniquestrings: ["X", "d"]});
accountModel4.save(); // OK => But i Want this to faile because "d" is alreay in use.

2 个答案:

答案 0 :(得分:5)

根据this MongoDB Doc,没有办法强制MongoDB在单个文档中强制执行唯一的索引策略,但是有一种方法可以在中强制执行单独的文件。

db.collection.createIndex("a.b");

...将在a.b ...

上强制执行这些内容的唯一性
db.collection.insert({ a: [{b: 1}] });
db.collection.insert({ a: [{b: 1}] });

...但不会强制执行这个唯一性......

db.collection.insert({ a: [{b: 1},{b: 1}] ]);

... 但是如果您严格使用<{3}} 索引......

db.collection.upsert({ $addToSet: { a: { b: 1 } } });

...并且你没有抛出异常而妥协,而是悄悄地忽略了重复,这不是你想要的,而是更接近。

到目前为止,我们已经涵盖了$addToSet中已回答的内容,但请继续阅读,也许您会得到自己所追求的内容。

现在,要实现您对本机MongoDB请求的要求是不可能的,但您可以ensureIndex并使用another SO question查找索引数组并在发现时抛出错误它,否则按照上面的说明进行升级。

因此...

// Ensure index
db.collection.createIndex({ 'a.b': 1 });

// Test for existence and throws up if not unique
function insertUnique(newVal) {
  var exists = db.collection.find({'a.b': newVal});
  if (exists) {
    throw "Element is not unique in the collection: " + newVal;
  } else {
    db.collection.upsert({ $addToSet: { a: { b: 1 } } });
  }
}

// Use it later...
try {
  insertUnique(1);
  insertUnique(1); // it should barf
} catch (e) {
  console.warn(e);
}

最后,根据您使用的客户端,您可以使用insertUnique方法扩展原型(在JS中),很快您就会忘记您无法执行此操作从...开始。

答案 1 :(得分:5)

如果您愿意将唯一值存储在其他集合中,则可能会这样做。它的结构如下:

{ "uniquestring" : "a", "account" : 111 }
{ "uniquestring" : "b", "account" : 111 }
{ "uniquestring" : "c", "account" : 111 }
{ "uniquestring" : "d", "account" : 222 }
{ "uniquestring" : "e", "account" : 222 }
{ "uniquestring" : "f", "account" : 222 }

我不是Mongoose的专家,但我相信您可以定义模型以将集合链接在一起,此处的帐户字段引用帐户集合的_id字段。

现在,您可以使用简单的索引来强制执行唯一性:

db.uniquestrings.createIndex( { "uniquestring" : 1 } , { unique : true } )

现在,您的应用在保存数据时还需要做更多的工作(它需要保存到uniquestrings集合以及帐户集合),但是现在您可以使用数据库级别来实现这些数据的唯一性数据库中的字符串。

任何有更多关于如何在猫鼬中实施和使用此类模型的详细知识的人都欢迎PS编辑。