如何在Mongoose模式中创建多个唯一数据点?

时间:2018-07-02 20:02:43

标签: javascript mongodb indexing mongoose

我正在使用express和mongoose / mongo db,并且有一个用户文档,其中包含两个我想唯一的数据点(电子邮件和用户名)。我的问题是我的注册过程包含多个步骤,因此先保存电子邮件,然后再保存用户名。我有一个错误,如果用户在步骤1和2之间,则其他用户无法注册,因为我收到错误消息,指出他们尝试保存的用户名或电子邮件不是唯一的。如果介于第1步和第2步之间的用户完成了操作,则另一个用户可以创建一封电子邮件,并且可以正常工作。我目前正在做的是复合索引,并以某种方式关联了这两个字段。我需要一个解决方案,如果mongo db尝试保存该字段,但mongo db返回一个错误,但是该字段中已有另一个用户的相同数据。我已经做了自己的检查,通过搜索该电子邮件并确保在搜索未返回任何内容时进行保存来确保其唯一性。这样做的问题是它是异步的,用户可能同时提交两次相同的电子邮件或用户名,然后创建。我想对两个字段分别使用mongo唯一索引(不以任何方式链接),以便mongo如果不是唯一的,则将返回错误,从而解决了这种情况。我在这里做错了什么?如何使这些字段唯一但彼此之间没有关联?这是我的代码的要点:

const UserSchema = new mongoose.Schema({
 email: {
  type: String,
  required: true,
  trim: true
},
 username: {
  type: String,
  required: false,
  trim: true
}
})

UserSchema.index({
  email: 1,
  username: 1
}, {unique: true});

2 个答案:

答案 0 :(得分:1)

猫鼬使您能够拥有唯一的字段。 当这些条件不匹配时,它将返回错误。

例如:     E11000 duplicate key error index in mongodb mongoose

您可以更新代码,例如:

const UserSchema = new mongoose.Schema({
 email: {
  type: String,
  required: true,
  unique: true,
  trim: true
},
 username: {
  type: String,
  required: false,
  unique: true,
  trim: true
}
})

答案 1 :(得分:0)

您看到的问题是由于您尚未为sparse字段创建partialusername索引。

请勿执行此操作

// Mongoose Schema Version
const UserSchema = new mongoose.Schema({
 email: {
  type: String,
  required: true,
  unique: true,
  trim: true
},
 username: {
  type: String,
  required: false,
  unique: true,
  trim: true
}
})

// Mongo Version
db.users.createIndex({ email: 1 }, { unique: true });
db.users.createIndex({ username: 1}, { unique: true });

// Mongoose Verion (Users is a Schema)
UserSchema.index({ email: 1 }, { unique: true });
UserSchema.index({ username: 1 }, { unique: true });

您将遇到以下问题:

  1. 插入{email: 'me@email.com'}
  2. 尝试插入{email: 'you@email.com'}-失败!

在MongoDB中,您有两条记录试图将username设置为null。无论它们是显式设置还是隐式设置(通过省略username),索引在冲突时将无法存储两个null。这就是为什么如果您为字段设置单独的索引,则数据库会被粘住,直到用户完成第一步,这允许另一位用户进入第一步并提交null用户名,但再次输入阻止数据库。可以使用复合索引来解决此问题,但是这会引起其他问题,例如用户将相同的电子邮件注册到两个用户名。

针对MongoDB 3.2或更高版本执行此操作

  

部分索引仅索引符合指定过滤器表达式的集合中的文档。通过索引集合中文档的子集,部分索引具有更低的存储需求,并降低了索引创建和维护的性能成本。

// Mongo Version
db.users.createIndex({ email: 1 }, { unique: true });
db.users.createIndex({ username: 1}, { unique: true, partialFilterExpression: { username: { $exists: true } } });

// Mongoose Verion (Users is a Schema)
UserSchema.index({ email: 1 }, { unique: true });
UserSchema.index({ username: 1 }, { unique: true, partialFilterExpression: { username: { $exists: true } } });

对于3.2之前的MongoDB,请执行此操作

  

稀疏索引仅包含具有索引字段的文档的条目,即使索引字段包含空值也是如此。索引会跳过缺少索引字段的所有文档。索引是“稀疏的”,因为它不包括集合的所有文档。相比之下,非稀疏索引包含一个集合中的所有文档,为那些不包含索引字段的文档存储空值。

// Mongoose Schema Version
const UserSchema = new mongoose.Schema({
 email: {
  type: String,
  required: true,
  unique: true,
  trim: true
},
 username: {
  type: String,
  required: false,
  unique: true,
  sparse: true,
  trim: true
}
})

// Mongo Version
db.users.createIndex({ email: 1 }, { unique: true });
db.users.createIndex({ username: 1}, { unique: true, sparse: true });

// Mongoose Verion (Users is a Schema)
UserSchema.index({ email: 1 }, { unique: true });
UserSchema.index({ username: 1 }, { unique: true, sparse: true });
  

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