目前我有两个几乎相同的架构:
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);
答案 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字段和选项合并。 。
举例说明这一点(假设您要共享或扩展已启用字段email
和is_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
});
现在UserSchema
和AdminSchema
将同时包含email
和is_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);