猫鼬:扩展模式

时间:2013-08-19 15:13:31

标签: node.js schema mongoose

目前我有两个几乎相同的架构:

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},

});

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

不幸的是,上面的代码并不是很干,因为它们几乎相同。因此,我想知道是否可以基于adminSchema构建userSchema。 E.g:

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);

9 个答案:

答案 0 :(得分:29)

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 :(得分: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
});

回答第二个问题,是,您可以add()字段。因此,要修改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 :(得分:4)

您可以扩展原始的架构#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

答案 3 :(得分:2)

要添加到此讨论中,您还可以使用自定义基础架构定义覆盖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
});

答案 4 :(得分:2)

我刚刚发布了mongoose-super npm module。虽然我做了一些测试,但仍处于试验阶段。我有兴趣知道它是否适用于我的SO用户的应用程序!

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

继承便利功能只使用discriminator method

答案 5 :(得分:1)

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

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您可以以任何方式扩展蓝图,而无需修改同一对象。

答案 6 :(得分: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});

在这种情况下,扩展会创建一个全新的架构定义。

答案 7 :(得分:0)

您可以创建一个Schema Factory函数,该函数接受一个Schema定义和可选 Schema选项,然后将传入的Schema定义和选项与要在各个模式中共享的Schema字段和选项合并。 。 举例说明这一点(假设您要共享或扩展已启用字段emailis_verifiedtimestamps选项的架构):

// 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选项以及模式字段以及您传递的选项。

答案 8 :(得分:0)

扩展猫鼬模式的最简单方法

import { model, Schema } from 'mongoose';

const ParentSchema = new Schema({
  fromParent: Boolean
});

const ChildSchema = new Schema({
  ...ParentSchema.obj,
  fromChild: Boolean // new properties come up here
});

export const Child = model('Child', ChildSchema);