如何处理 Mongoose 中的 ENUM 值?

Posted

技术标签:

【中文标题】如何处理 Mongoose 中的 ENUM 值?【英文标题】:What to do with ENUM values in Mongoose? 【发布时间】:2017-08-26 19:50:21 【问题描述】:

背景

我有一个 Mongoose 模式,它定义了给定对象可以具有的一组可能值。

const mongoose = require("mongoose");

const COUNTRIES = ["ES", "PT", "US", "FR", "UK"];
const GENDERS = ["M", "F"];

const surveySchema = 
    subject:  type: String, required: true ,
    country:  type: String, enum: COUNTRIES ,
    target: 
        gender:  type: String, enum: GENDERS 
    
;

module.exports = new mongoose.Schema(surveySchema);;
module.exports.modSchema = surveySchema;

为什么我不喜欢ENUM

我个人不喜欢ENUM 值,因为如果我向ENUM 添加另一个值,我必须再次重新编译整个应用程序并进行部署。

我猜想对于ENUM,比如性别,它永远不会改变,这不是问题。

但是,对于国家/地区,我的 SQL 方面告诉我应该存储它们,因为如果您的业务不断增长,您可能会扩展到其他国家/地区。

问题

我的问题是我不知道如何在架构级别告诉 Mongoose,国家/地区的唯一允许值必须是 ["ES", "PT", "US", "FR", "UK"]

我想我可以创建一个集合国家,但是我缺乏如何连接它们的知识。我必须使用异步验证器吗?

您将如何处理可以更改的ENUM

【问题讨论】:

ENUM 始终处于模式级别。您可以随时在枚举列表中添加删除国家/地区。只需要重新启动应用程序。将其添加到枚举后。 或者在生产环境中部署。这是一个很大的不。如果您每次需要额外的国家/地区、产品、书籍等时都部署一个新版本的应用程序,那么您将注定失败。 【参考方案1】:

只想补充一件事,@Shawon 的回答很棒。知道有两种方法可以使验证器异步。如上所示,如果您指定 isAsync 标志,然后 Mongoose 会将该回调传递给您的验证器函数,以便在您的验证结束时调用,但您应该只在您没有返回承诺时使用该标志。如果您从自定义验证器返回一个 Promise,Mongoose 将知道它是异步的,并且不再需要 isAsync 标志。

Mongoose 异步自定义验证器文档可在此处找到,它很好地展示了这两个示例,很棒的文档。 http://mongoosejs.com/docs/validation.html#async-custom-validators

【讨论】:

【参考方案2】:

您可以使用管理面板向国家/地区集合添加更多国家/地区。正如您所说的 COUNTRIES 数组可以增长,您可以使用另一个集合从管理面板按需添加更多国家。

当您要在调查中添加/保存新记录时,您可以触发预保存挂钩到 mongo 进行验证。

假设我们有另一个这样的国家模式。


 countries: [String]

这是该场景的示例代码。

const mongoose = require("mongoose");

const GENDERS = ["M", "F"];

const surveySchema = 
    subject:  type: String, required: true ,
    country:  type: String,
    target: 
        gender:  type: String, enum: GENDERS 
    
;

var Survey = new mongoose.Schema(surveySchema);

Survey.pre('save',function(next)
  var me = this;
  CountryModel.find(,(err,docs)=>
    (docs.countries.indexOf(me.country) >=0) ? next() : next(new Error('validation failed'));
  );
);

这样您就可以处理动态国家添加,而无需更改国家/地区数组并重新部署您的整个服务器。

使用自定义验证器

const mongoose = require("mongoose");

const GENDERS = ["M", "F"];

const surveySchema = 
        subject: 
            type: String,
            required: true
        ,
        country: 
            type: String,
            validate: 
                isAsync: true,
                validator: function(arg, cb) 
                    CountryModel.find(, (err, docs) => 
                                if (err) 
                                    cb(err);
                                 else 
                                    cb(docs.countries.indexOf(arg) >= 0);
                                
                            
                        ,
                        message: 'VALUE is not a valid country'
                
            ,
            target: 
                gender:  type: String, enum: GENDERS 
            
        ;

在回调中保存调查数据时会出错..

ServeyModel.save((err,doc)=>
if(err)
console.log(err.errors.country.message);
//Error handle
else 
//TODO

);

【讨论】:

async custom validator 钩子在这里比保存更合适。这允许在对象级别进行验证,而无需将其保存到数据库中。 这也能解决问题。但是您必须以一种或另一种方式在某处验证值。预保存的一个优点是错误将由保存方法的回调错误处理程序直接处理。在自定义验证的情况下,您必须手动将错误传递给下一个瀑布或其他错误处理程序。 你能发布一个带有异步验证器的解决方案来进行比较吗? 抱歉回复晚了。我已经使用自定义验证器编辑了我的答案。 **默认情况下,所有验证器都注册为预保存挂钩。 感谢您的回复!

以上是关于如何处理 Mongoose 中的 ENUM 值?的主要内容,如果未能解决你的问题,请参考以下文章

处理 Mongoose 验证错误——在哪里以及如何处理?

(Mongo/Mongoose) 如何处理等待多个查询的结果

如何处理 MVC nodejs 服务上的角色(jsonwebtoken,mongoose)

运行 Mongoose 时执行 ajax 调用时如何处理 Node.js 发现

连接停止时如何处理猫鼬数据库

mongodb 连接如何处理 NodeJS express 服务器中的并发请求?