如何处理Mongoose中的ENUM值?

时间:2017-04-01 16:33:55

标签: node.js mongodb mongoose enums schema

背景

我有一个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

2 个答案:

答案 0 :(得分:6)

您可以使用管理面板向国家/地区集合添加更多国家/地区。正如您所说,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
}
});

答案 1 :(得分:0)

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

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