详解 Mongoose 中的 unique 唯一索引

unique 选项告诉 Mongoose 每个文档对于给定路径必须具有唯一值。例如下面是如何告诉 Mongoose 用户的 email 必须是唯一的。

const mongoose = require(mongoose);

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    unique: true // `email` must be unique
  }
});
const User = mongoose.model(User, userSchema);

如果您尝试创建两个相同的用户 email,你会得到一个 重复键错误

// Throws `MongoError: E11000 duplicate key error collection...`
await User.create([
  { email: test@google.com },
  { email: test@google.com }
]);

const doc = new User({ email: test@google.com });
// Throws `MongoError: E11000 duplicate key error collection...`
await doc.save();

更新也可能引发重复键错误。 如果您创建具有唯一电子邮件地址的用户,然后将其电子邮件地址更新为非唯一值,您将收到相同的错误。

await User.create({ email: test2@google.com });

// Throws `MongoError: E11000 duplicate key error collection...`
await User.updateOne({ email: test2@google.com }, { email: test@google.com });

索引,不是验证器

一个常见的问题是 unique 选项告诉 Mongoose 定义一个 唯一索引 。 这意味着 Mongoose 不会 检查唯一性 validate()

await User.create({ email: sergey@google.com });

const doc = new User({ email: sergey@google.com });
await doc.validate(); // Does not throw an error

事实是 unique 定义索引而不是验证器也很重要,编写自动化测试。 如果你删除数据库 User 模型已连接,您还将删除 unique 索引,您将能够保存重复项。

await mongoose.connection.dropDatabase();

// Succeeds because the `unique` index is gone!
await User.create([
  { email: sergey@google.com },
  { email: sergey@google.com }
]);

在生产中,您通常不会删除数据库,因此这在生产中很少出现问题。

在编写 Mongoose 测试时,我们通常建议使用 deleteMany() 清除测试之间的数据,而不是 dropDatabase(),这可确保您删除所有文档,而无需清除数据库级别的配置,例如索引和排序规则。 deleteMany() 也比 dropDatabase()

但是,如果您选择在测试之间删除数据库,则可以使用 Model.syncIndexes() 函数 重建所有唯一索引的

await mongoose.connection.dropDatabase();

// Rebuild all indexes
await User.syncIndexes();

// Throws `MongoError: E11000 duplicate key error collection...`
await User.create([
  { email: sergey@google.com },
  { email: sergey@google.com }
]);

处理 null

自从 null 是一个不同的值,您不能保存两个具有 null 电子邮件。 同样,您不能保存两个没有 email 值。

// Throws because both documents have undefined `email`
await User.create([
  {},
  {}
]);

// Throws because both documents have null `email`
await User.create([
  { email: null },
  { email: null }
]);

一种解决方法是使 email 财产 required,这不允许 nullundefined

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true // `email` must be unique
  }
});

如果你需要 email 是唯一的 ,除非 它没有被定义,你可以改为定义一个 稀疏的唯一 索引 email 如下所示。

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    // `email` must be unique, unless it isnt defined
    index: { unique: true, sparse: true }
  }
});

用户友好的重复键错误

要使 MongoDB E11000 错误消息对用户友好,您应该使用 mongoose -beautiful-unique-validation 插件

const schema = new Schema({ name: String });
schema.plugin(require(mongoose-beautiful-unique-validation));

const CharacterModel = mongoose.model(Character, schema);

const doc = await CharacterModel.create({ name: Jon Snow });

try {
  // Try to create a document with the same `_id`. This will always fail
  // because MongoDB collections always have a unique index on `_id`.
  await CharacterModel.create(Object.assign({}, doc.toObject()));
} catch (error) {
  // Path `_id` (5cc60c5603a95a15cfb9204d) is not unique.
  error.errors[_id].message;
}
© 版权声明
THE END
喜欢就支持一下吧
点赞359 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容