mongoDB / mongoose:如果不为null,则为唯一

时间:2011-10-31 14:26:06

标签: mongodb node.js mongoose

我想知道是否有办法强制使用唯一的集合条目,但仅限于条目不为空。 Ë 示例模式:

var UsersSchema = new Schema({
    name  : {type: String, trim: true, index: true, required: true},
    email : {type: String, trim: true, index: true, unique: true}
});
在这种情况下,

'email'不是必需的,但如果保存了'email',我想确保此条目是唯一的(在数据库级别上)。

空条目似乎得到值'null'所以每个条目都没有电子邮件与'unique'选项崩溃(如果有不同的用户没有电子邮件)。

现在我正在应用程序级别解决它,但是希望保存该数据库查询。

THX

4 个答案:

答案 0 :(得分:130)

从MongoDB v1.8 +开始,您可以通过在定义索引时将sparse选项设置为true来获得确保唯一值的所需行为,但允许多个文档而不使用该字段。如:

email : {type: String, trim: true, index: true, unique: true, sparse: true}

或者在shell中:

db.users.ensureIndex({email: 1}, {unique: true, sparse: true});

请注意,唯一的稀疏索引仍然不允许多个文档的email字段的null,只有多个文档没有 email字段。

请参阅http://docs.mongodb.org/manual/core/index-sparse/

答案 1 :(得分:25)

TL;博士

是的,可以将多个文档的字段设置为null或未定义,同时强制使用唯一的“实际”值。

要求

  • MongoDB v3.2 +。
  • 提前了解您的具体价值类型(例如,在string时,始终为objectnull

如果您对细节不感兴趣,请随时跳至implementation部分。

更长的版本

要补充@ Nolan的答案,从MongoDB v3.2开始,您可以使用带有过滤表达式的部分唯一索引。

部分过滤器表达式有局限性。它只能包含以下内容:

  
      
  • 等式表达式(即字段:值或使用$eq运算符),
  •   
  • $exists: true表达,
  •   
  • $gt$gte$lt$lte表达式,
  •   
  • $type表达式,
  •   仅在顶层
  • $and运营商
  •   

这意味着无法使用普通表达式{"yourField"{$ne: null}}

但是,假设您的字段始终使用相同类型,则可以使用$type expression

{ field: { $type: <BSON type number> | <String alias> } }

MongoDB v3.6增加了对指定多种可能类型的支持,这些类型可以作为数组传递:

{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }

这意味着当null不是email时,它允许该值为多种类型中的任意一种。

因此,如果我们希望允许下面示例中的string字段接受binary data或者$type值,那么适当的{email: {$type: ["string", "binData"]}} 表达式将是:

const UsersSchema = new Schema({
  name: {type: String, trim: true, index: true, required: true},
  email: {
    type: String, trim: true, index: {
      unique: true,
      partialFilterExpression: {email: {$type: "string"}}
    }
  }
});

实施

猫鼬

您可以在mongoose架构中指定它:

User.collection.createIndex("email", {
  unique: true,
  partialFilterExpression: {
    "email": {
      $type: "string"
    }
  }
});

或直接将其添加到集合(使用本机node.js驱动程序):

db.collection('users').createIndex({
    "email": 1
  }, {
    unique: true,
    partialFilterExpression: {
      "email": {
        $type: "string"
      }
    }
  },
  function (err, results) {
    // ...
  }
);

本地mongodb驱动程序

使用collection.createIndex

db.users.createIndex({
  "email": 1
}, {
  unique: true, 
  partialFilterExpression: {
    "email": {$type: "string"}
  }
})

mongodb shell

使用db.collection.createIndex

null

这将允许使用<data> <variable name="user" type="ObservableMap&lt;String,Object&gt;"/> </data> 电子邮件插入多个记录,或者根本不插入电子邮件字段,但不能使用相同的电子邮件字符串。

答案 2 :(得分:4)

快速更新那些研究此主题的人。

所选答案可行,但您可能需要考虑使用部分索引。

  

在3.2版中更改:从MongoDB 3.2开始,MongoDB提供了   创建部分索引的选项。部分索引提供了超集   稀疏索引的功能。如果您使用的是MongoDB 3.2或   之后,部分索引应优先于稀疏索引。

更多doco部分索引:https://docs.mongodb.com/manual/core/index-partial/

答案 3 :(得分:2)

实际上,只有“电子邮件”字段不存在的第一个文档才能成功保存。不存在“email”的后续保存将在发出错误时失败(请参阅下面的代码段)。因此,请查看位于http://www.mongodb.org/display/DOCS/Indexes#Indexes-UniqueIndexes的有关唯一索引和缺失密钥的MongoDB官方文档。

  // NOTE: Code to executed in mongo console.

  db.things.ensureIndex({firstname: 1}, {unique: true});
  db.things.save({lastname: "Smith"});

  // Next operation will fail because of the unique index on firstname.
  db.things.save({lastname: "Jones"});

根据定义,唯一索引只能允许一个值只存储一次。如果将null视为一个这样的值,则只能插入一次!通过在应用程序级别确保和验证它,您的方法是正确的。这就是它可以做到的。

您可能还想阅读此http://www.mongodb.org/display/DOCS/Querying+and+nulls