提问者:小点点

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


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

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

例如。

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

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

例如。通过从Mongo抛出重复错误来防止account(222).uniquestrings.push(“a”);

数组中的唯一性不够
²数组中的每个项必须在整个集合中唯一

更新1:

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

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

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

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个答案

匿名用户

如果您愿意将唯一的值存储在不同的集合中,这可能是可能的。它的结构如下:

{ "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方面的专家,但我相信您可以定义模型将集合链接在一起,这里的account字段引用accounts集合的id字段。

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

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

现在,您的应用程序在保存数据时将有更多的工作要做(它需要保存到uniquestrings集合和accounts集合),但是您现在已经在数据库中对这些字符串的唯一性进行了数据库级的强制执行。

欢迎任何对如何在Mongoose中实现和使用此类模型有更详细了解的人进行PS编辑。

匿名用户

根据这个MongoDB文档,没有办法强制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}] ]);

。。。但是如果严格地将$AddToset与索引一起使用。。。

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

。。。你可以通过不抛出异常来妥协,而是让upsert悄悄地忽略重复项,这不是你想要的,而是更接近它。

到目前为止,我们已经讨论了另一个So问题的答案,但是继续阅读,也许你会得到你想要的。

现在,要实现本机MongoDB请求所要求的功能,不可能开箱即用,但是您可以EnsureIndex并使用覆盖查询来查找索引数组,如果找到它就抛出错误,否则就按照上面的指示进行更新。

所以。。。

// 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中),但很快您就会忘记从一开始就不能这样做。