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

我想知道是否有办法强制一个唯一的收集入口, 但只有入境不为空 。 e示例架构:

var UsersSchema = new Schema({ name : {type: String, trim: true, index: true, required: true}, email : {type: String, trim: true, index: true, unique: true} }); 

在这种情况下,“电子邮件”不是必需的,但如果保存“电子邮件”,我想确保此项是唯一的(在数据库级别)。

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

现在我正在解决它的应用程序级别,但很想保存数据库查询。

谢谢

从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/

TL;博士

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

要求

  • MongoDB v3.2 +。
  • 当你的字段有一个实际的值,它应该是一个特定的types(例如,总是一个string)。

怎么样? 转到下面的实现细节。

更长的版本

为了补充@Nolan的回答,从MongoDB v3.2开始,你可以使用带有过滤expression式的部分唯一索引。

部分过滤expression式有局限性。 它只能包括以下内容:

  • 相等expression式(即字段:值或使用$eq运算符),
  • $exists: trueexpression式,
  • $gt$gte$lt$lteexpression式,
  • $typeexpression式,
  • $and操作员只在顶层

这意味着不能使用简单的expression式{"yourField"{$ne: null}}

但是,假设您的字段总是使用相同的types ,则可以使用$typeexpression式 。

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

履行

mongoose

你可以在一个mongoose模式中指定它:

 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'}} } } }); 

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

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

本机的mongodb驱动程序

使用collection.createIndex

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

mongodbshell

使用db.collection.createIndex

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

这将允许用null电子邮件插入多个logging,或根本没有电子邮件字段,但不能使用相同的电子邮件string。

实际上,只有第一个“email”字段不存在的文件才能成功保存。 随后的保存“email”不存在的地方将会出错(见下面的代码段)。 出于这个原因,在http://www.mongodb.org/display/DOCS/Indexes#Indexes-UniqueIndexes处查看MongoDB官方文档中关于Unique Indexes和Missing Keys的内容。

  // 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是一个这样的值,它只能被插入一次! 通过在应用程序级别确保和validation您的方法,您是正确的。 这是如何做到的。

您也可以阅读http://www.mongodb.org/display/DOCS/Querying+and+nulls

只是对那些研究这个主题的快速更新。

选定的答案将起作用,但您可能需要考虑使用部分索引。

在版本3.2中更改:从MongoDB 3.2开始,MongoDB提供了创build部分索引的选项。 部分索引提供了稀疏索引function的超集。 如果您使用的是MongoDB 3.2或更高版本,则部分索引应优先于稀疏索引。

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