首页 文章

猫鼬:扩展模式

提问于
浏览
20

目前我有两个几乎相同的模式:

var userSchema = mongoose.Schema({

    email: {type: String, unique: true, required: true, validate: emailValidator},
    passwordHash: {type: String, required: true},

    firstname: {type: String, validate: firstnameValidator},
    lastname: {type: String, validate: lastnameValidator},
    phone: {type: String, validate: phoneValidator},

});

var adminSchema = mongoose.Schema({

    email: {type: String, unique: true, required: true, validate: emailValidator},
    passwordHash: {type: String, required: true},

    firstname: {type: String, validate: firstnameValidator, required: true},
    lastname: {type: String, validate: lastnameValidator, required: true},
    phone: {type: String, validate: phoneValidator, required: true},

});

他们唯一的区别在于验证:用户不需要名字,姓氏或电话 . 但是,管理员必须定义这些属性 .

不幸的是,上面的代码不是很干,因为它们几乎相同 . 因此,我想知道是否有可能基于 userSchema 构建 adminSchema . 例如 . :

var adminSchema = mongoose.Schema(userSchema);
adminSchema.change('firstname', {required: true});
adminSchema.change('lastname', {required: true});
adminSchema.change('phone', {required: true});

显然这只是伪代码 . 这样的事情可能吗?

另一个非常相似的问题是,是否可以基于另一个创建新模式,并为其添加更多属性 . 例如:

var adminSchema = mongoose.Schema(userSchema);
    adminSchema.add(adminPower: Number);

8 回答

  • 10

    有些人在其他地方suggested using utils.inherits来扩展模式 . 另一种简单的方法是简单地设置一个带有设置的对象并从中创建Schema,如下所示:

    var settings = {
      one: Number
    };
    
    new Schema(settings);
    
    settings.two = Number;
    new Schema(settings);
    

    虽然它有点难看,因为你正在修改同一个对象 . 此外,我希望能够扩展插件和方法等 . 因此,我首选的方法如下:

    function UserSchema (add) {
      var schema = new Schema({
        someField: String
      });
    
      if(add) {
        schema.add(add);
      }
    
      return schema;
    }
    
    var userSchema = UserSchema();
    var adminSchema = UserSchema({
      anotherField: String
    });
    

    这恰好回答了你的第二个问题 yes, you can add() fields . 因此,要修改Schema的某些属性,上述函数的修改版本将解决您的问题:

    function UserSchema (add, nameAndPhoneIsRequired) {
      var schema = new Schema({
        //...
        firstname: {type: String, validate: firstnameValidator, required: nameAndPhoneIsRequired},
        lastname: {type: String, validate: lastnameValidator, required: nameAndPhoneIsRequired},
        phone: {type: String, validate: phoneValidator, required: nameAndPhoneIsRequired},
      });
    
      if(add) {
        schema.add(add);
      }
    
      return schema;
    }
    
  • 2

    Mongoose 3.8.1现在支持歧视者 . 一个样本,从这里:http://mongoosejs.com/docs/api.html#model_Model.discriminator

    function BaseSchema() {
      Schema.apply(this, arguments);
    
      this.add({
        name: String,
        createdAt: Date
      });
    }
    util.inherits(BaseSchema, Schema);
    
    var PersonSchema = new BaseSchema();
    var BossSchema = new BaseSchema({ department: String });
    
    var Person = mongoose.model('Person', PersonSchema);
    var Boss = Person.discriminator('Boss', BossSchema);
    
  • 1

    您可以扩展原始 Schema#obj

    const AdminSchema = new mongoose.Schema({},Object.assign(UserSchema.obj,))

    例:

    const mongoose = require('mongoose');
    
    const UserSchema = new mongoose.Schema({
      email: {type: String, unique: true, required: true},
      passwordHash: {type: String, required: true},
    
      firstname: {type: String},
      lastname: {type: String},
      phone: {type: String}
    });
    
    // Extend function
    const extend = (Schema, obj) => (
      new mongoose.Schema(
        Object.assign({}, Schema.obj, obj)
      )
    );
    
    // Usage:
    const AdminUserSchema = extend(UserSchema, {
      firstname: {type: String, required: true},
      lastname: {type: String, required: true},
      phone: {type: String, required: true}
    });
    
    const User = mongoose.model('users', UserSchema);
    const AdminUser = mongoose.model('admins', AdminUserSchema);
    
    const john = new User({
      email: 'user@site.com',
      passwordHash: 'bla-bla-bla',
      firstname: 'John'
    });
    
    john.save();
    
    const admin = new AdminUser({
      email: 'admin@site.com',
      passwordHash: 'bla-bla-bla',
      firstname: 'Henry',
      lastname: 'Hardcore',
      // phone: '+555-5555-55'
    });
    
    admin.save();
    // Oops! Error 'phone' is required
    

    或者使用此npm模块使用相同的方法:

    const extendSchema = require('mongoose-extend-schema'); // not 'mongoose-schema-extend'
    
    const UserSchema = new mongoose.Schema({
      firstname: {type: String},
      lastname: {type: String}
    });
    
    const ClientSchema = extendSchema(UserSchema, {
      phone: {type: String, required: true}
    });
    

    检查github repo https://github.com/doasync/mongoose-extend-schema

  • 0

    要添加到此讨论中,您还可以使用自定义基础架构定义覆盖mongoose.Schema . 为了代码兼容性,添加允许在没有 new 的情况下实例化Schema的if语句 . 虽然这很方便,但在公共包中执行此操作之前要三思而后行 .

    var Schema = mongoose.Schema;
    
    var BaseSyncSchema = function(obj, options) {
    
        if (!(this instanceof BaseSyncSchema))
            return new BaseSyncSchema(obj, options);
    
        Schema.apply(this, arguments);
    
        this.methods.update = function() {
            this.updated = new Date();
        };
    
        this.add({
            updated: Date
        });
    };
    util.inherits(BaseSyncSchema, Schema);
    
    // Edit!!!
    // mongoose.Schema = BaseSyncSchema; <-- Does not work in mongoose 4
    // Do this instead:
    Object.defineProperty(mongoose, "Schema", {
        value: BaseSyncSchema,
        writable: false
    });
    
  • 29

    我刚刚发表了mongoose-super npm module . 虽然我做了一些测试,但仍处于试验阶段 . 我很想知道它是否适合我的SO用户的应用程序!

    该模块提供了inherit()便捷函数,该函数基于父模型和子模式扩展返回子Mongoose.js模型 . 它还使用super()方法扩充模型以调用父模型方法 . 我添加了这个功能,因为它是我在其他扩展/继承库中遗漏的东西 .

    继承便利功能只使用discriminator method .

  • 4

    所有这些答案看起来都相当复杂,扩展辅助函数或扩展方法应用于模式或使用插件/鉴别器 . 我使用了以下解决方案,它简单,干净且易于使用 . 它定义了基本模式的蓝图,然后使用蓝图构建实际模式:

    foo.blueprint.js

    module.exports = {
      schema: {
        foo: String,
        bar: Number,
      },
      methods: {
        fooBar() {
          return 42;
        },
      }
    };
    

    foo.schema.js

    const {schema, methods} = require('./foo.blueprint');
    const {Schema} = require('mongoose');
    const FooSchema = new Schema(foo);
    Object.assign(FooSchema.methods, methods);
    module.exports = FooSchema;
    

    bar.schema.js

    const {schema, methods} = require('./foo.blueprint');
    const {Schema} = require('mongoose');
    const BarSchema = new Schema(Object.assign({}, schema, {
      bar: String,
      baz: Boolean,
    }));
    Object.assign(BarSchema.methods, methods);
    module.exports = BarSchema;
    

    您可以按原样使用原始模式的蓝图,并使用 Object.assign ,您可以以任何方式扩展蓝图,而无需修改同一对象 .

  • 0

    我没有要求歧视,因为我试图扩展子文档模式,无论如何都存储为父文档的一部分 .

    我的解决方案是将“扩展”方法附加到作为基本模式的模式,以便您可以使用基本模式本身或基于它生成新模式 .

    ES6代码如下:

    'use strict';
    
    //Dependencies
    let Schema = require('mongoose').Schema;
    
    //Schema generator
    function extendFooSchema(fields, _id = false) {
    
      //Extend default fields with given fields
      fields = Object.assign({
        foo: String,
        bar: String,
      }, fields || {});
    
      //Create schema
      let FooSchema = new Schema(fields, {_id});
    
      //Add methods/options and whatnot
      FooSchema.methods.bar = function() { ... };
    
      //Return
      return FooSchema;
    }
    
    //Create the base schema now
    let FooSchema = extendFooSchema(null, false);
    
    //Expose generator method
    FooSchema.extend = extendFooSchema;
    
    //Export schema
    module.exports = FooSchema;
    

    您现在可以按原样使用此架构,或根据需要“扩展”它:

    let BazSchema = FooSchema.extend({baz: Number});
    

    在这种情况下,扩展会创建一个全新的Schema定义 .

  • 2

    您可以创建一个Schema Factory函数,该函数接受模式定义和可选模式选项,然后将传递的模式定义和选项与要在模式之间共享的模式字段和选项合并 . 说明此示例(假设您要共享或扩展具有字段 emailis_verified 以及启用 timestamps 选项的架构):

    // schemaFactory.js
    const mongoose = require('mongoose');
    
    const SchemaFactory = (schemaDefinition, schemaOptions) => {
      return new mongoose.Schema({
        {
          email: {type: String, required: true},
          is_verified: {type: Boolean, default: false},
          // spread/merge passed in schema definition
          ...schemaDefinition
        }
      }, {
        timestamps: true,
        // spread/merge passed in schema options
        ...schemaOptions
      })
    }
    module.exports = SchemaFactory;
    

    然后可以使用以下命令调用 SchemaFactory 函数:

    // schemas.js
    const SchemaFactory = require("./schemaFactory.js")
    
    const UserSchema = SchemaFactory({
      first_name: String,
      password: {type: String, required: true}
    });
    
    const AdminSchema = SchemaFactory({
      role: {type: String, required: true}
    }, {
      // we can pass in schema options to the Schema Factory
      strict: false
    });
    

    现在 UserSchemaAdminSchema 将包含 emailis_verified 字段以及启用了 timestamps 选项,以及传递的架构字段和选项 .

相关问题