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

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

不幸的是,上面的代码不是很干,因为它们几乎是相同的。 因此,我想知道是否有可能build立一个基于userSchema 。 例如:

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

显然这只是伪代码。 是这样的可能吗?

另一个非常相似的问题是,如果可以基于另一个创build新的模式,并添加更多的属性。 例如:

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

有些人在其他地方build议使用utils.inherits来扩展模式。 另一个简单的方法是简单地设置一个对象的设置,并从中创build模式,如下所示:

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

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

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

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

我刚刚发布了一个mongoose-super npm模块 。 虽然我做了一些testing,但仍处于实验阶段。 我很想知道它是否适用于我的SO用户的应用程序!

该模块提供了一个inherit()便利函数,该函数根据父模型和子模式扩展返回一个子Mongoose.js模型。 它还用super()方法来扩充模型来调用父模型方法。 我添加了这个function,因为这是我在其他扩展/inheritance库中错过的东西。

inheritance便利函数只是使用鉴别器方法 。

我不需要歧视,因为我试图扩展作为父文档的一部分存储的子文档模式。

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

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

在这种情况下扩展创build一个全新的模式定义。

你可以扩展原来的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回购https://github.com/doasync/mongoose-extend-schema

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

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