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

Posted

技术标签:

【中文标题】在一个集合的所有文档中具有唯一值的数组【英文标题】:array with unique values over all documents of one collection 【发布时间】:2017-08-16 09:04:20 【问题描述】:

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

问题: 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:

更多示例。受影响的 Schema 条目如下所示:

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.

【问题讨论】:

我不确定问题出在哪里......我刚刚做了一些测试,它在唯一索引(使用 mongo shell)下运行良好。能发一下有问题的js代码吗? 您是如何创建索引的? 【参考方案1】:

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

 "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 集合以及 accounts 集合),但您现在可以在数据库级别强制执行这些数据的唯一性字符串,跨数据库。

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

【讨论】:

有趣的模式。我试试看。【参考方案2】:

根据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] ]);

...但是如果你严格使用$addToSet 索引...

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

...您通过不抛出异常来妥协,而是 upsert 悄悄地忽略重复项,这不是您想要的,但更接近。

到目前为止,我们已经介绍了 another SO question 中的回答,但请继续阅读,也许你会得到你想要的。

现在,通过本机 MongoDB 请求实现您的要求是不可能开箱即用的,但您可以 ensureIndex 并使用 covered query 查找索引数组并在找到它时抛出错误,否则 upsert如上所述。

所以...

// 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 中),很快您就会忘记您一开始无法做到这一点。

【讨论】:

很好的解释。但我希望的解决方案不应该依赖于我自己的“isunique”检查。 当然...如果您愿意放弃对唯一字符串数组的要求,那么您应该按照@Vince Bowdren 的建议创建一个索引约束的关系哈希图。 :)

以上是关于在一个集合的所有文档中具有唯一值的数组的主要内容,如果未能解决你的问题,请参考以下文章

具有单个键的多个值的STL集合

在 SQL 中选择具有唯一列值的行

MongoDB查询以选择具有所有元素都匹配某些条件的数组的文档

尝试使用过滤器设置具有唯一值的数组失败

在 Mongoose Schema 中使用多个值的唯一文档

基于具有唯一值的数组创建多个动态选择过滤器以过滤 Vue.js 中的另一个数组