详解 Mongoose 中的 unique 唯一索引

发布于 2022-09-11 13:59:19 字数 4756 浏览 190 评论 0

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 isn't 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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

嘴硬脾气大

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

eins

文章 0 评论 0

世界等同你

文章 0 评论 0

毒初莱肆砂笔

文章 0 评论 0

初雪

文章 0 评论 0

miao

文章 0 评论 0

qq_zQQHIW

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文