模型的继承与扩展
模型继承的基本概念
Mongoose中的模型继承允许开发者基于现有模型创建新模型,同时保留父模型的属性和方法。这种机制在数据库设计中非常实用,特别是当多个集合共享相同基础字段时。Mongoose提供了两种主要的继承方式:原型继承(Prototypal Inheritance)和类继承(Class Inheritance)。
原型继承通过Schema的add
方法实现:
const baseSchema = new mongoose.Schema({
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
const userSchema = new mongoose.Schema({
username: String,
email: String
});
userSchema.add(baseSchema);
类继承则更接近传统OOP模式:
class BaseEntity {
constructor() {
this.createdAt = new Date();
this.updatedAt = new Date();
}
}
class User extends BaseEntity {
constructor(username, email) {
super();
this.username = username;
this.email = email;
}
}
鉴别器模式实现继承
Mongoose的鉴别器(Discriminator)是处理模型继承最强大的工具。它允许在同一个集合中存储不同类型的文档,通过__t
字段区分文档类型。这种方法特别适合多态数据场景。
基本实现示例:
const eventSchema = new mongoose.Schema({
time: Date,
location: String
}, { discriminatorKey: 'kind' });
const Event = mongoose.model('Event', eventSchema);
// 创建演唱会子模型
const Concert = Event.discriminator('Concert',
new mongoose.Schema({
artist: String,
ticketPrice: Number
})
);
// 创建会议子模型
const Conference = Event.discriminator('Conference',
new mongoose.Schema({
topic: String,
attendees: Number
})
);
查询时会自动处理类型识别:
// 查询所有事件
const events = await Event.find();
// 只查询演唱会
const concerts = await Concert.find();
Schema扩展方法
除了继承,Mongoose还支持通过插件(Plugin)机制扩展Schema功能。这种方式不改变继承链,而是横向扩展功能。
创建时间戳插件示例:
function timestampPlugin(schema) {
schema.add({
createdAt: Date,
updatedAt: Date
});
schema.pre('save', function(next) {
const now = new Date();
if (!this.createdAt) {
this.createdAt = now;
}
this.updatedAt = now;
next();
});
}
// 应用插件
const productSchema = new mongoose.Schema({
name: String,
price: Number
});
productSchema.plugin(timestampPlugin);
多级继承实现
Mongoose支持多级模型继承,可以构建复杂的继承层次结构。这在企业级应用中很常见,比如电子商务系统中的商品分类。
三级继承示例:
// 基础商品模型
const productSchema = new mongoose.Schema({
sku: String,
price: Number
});
// 电子产品继承商品
const electronicSchema = new mongoose.Schema({
warranty: Number
});
// 手机继承电子产品
const phoneSchema = new mongoose.Schema({
screenSize: Number,
os: String
});
const Product = mongoose.model('Product', productSchema);
const Electronic = Product.discriminator('Electronic', electronicSchema);
const Phone = Electronic.discriminator('Phone', phoneSchema);
查询时可以跨层级操作:
// 查询所有商品
const allProducts = await Product.find();
// 只查询电子产品
const electronics = await Electronic.find();
// 查询特定手机
const phones = await Phone.find({ os: 'Android' });
虚拟属性继承
虚拟属性(Virtuals)在继承模型中表现特殊,它们不会自动被子模型继承,需要显式处理。
虚拟属性继承示例:
const personSchema = new mongoose.Schema({
firstName: String,
lastName: String
});
personSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
const employeeSchema = new mongoose.Schema({
department: String,
position: String
});
// 继承虚拟属性
employeeSchema.virtual('fullName').get(personSchema.virtuals.fullName.get);
const Person = mongoose.model('Person', personSchema);
const Employee = Person.discriminator('Employee', employeeSchema);
静态方法与实例方法
方法继承是模型复用功能的重要方式。静态方法作用于模型本身,实例方法作用于文档实例。
方法继承示例:
const animalSchema = new mongoose.Schema({
name: String,
type: String
});
// 静态方法
animalSchema.statics.findByType = function(type) {
return this.find({ type });
};
// 实例方法
animalSchema.methods.speak = function() {
console.log(`${this.name} makes a sound`);
};
const dogSchema = new mongoose.Schema({
breed: String
});
// 继承方法
Object.assign(dogSchema.statics, animalSchema.statics);
Object.assign(dogSchema.methods, animalSchema.methods);
const Animal = mongoose.model('Animal', animalSchema);
const Dog = Animal.discriminator('Dog', dogSchema);
查询中间件继承
中间件(Middleware)在继承模型中的行为需要特别注意。pre和post钩子不会自动继承,需要在子模型中重新定义。
中间件处理示例:
const logSchema = new mongoose.Schema({
message: String,
level: String
});
logSchema.pre('save', function(next) {
console.log('About to save log:', this.message);
next();
});
const errorLogSchema = new mongoose.Schema({
stack: String
});
// 继承中间件
errorLogSchema.pre('save', logSchema.pre('save')[0]);
const Log = mongoose.model('Log', logSchema);
const ErrorLog = Log.discriminator('ErrorLog', errorLogSchema);
索引继承策略
索引不会自动从父模型继承到子模型。需要在每个子模型中显式定义索引,或者使用Schema合并技术。
索引处理示例:
const contentSchema = new mongoose.Schema({
title: { type: String, index: true },
body: String
});
const articleSchema = new mongoose.Schema({
author: { type: String, index: true },
tags: [String]
});
// 合并Schema并保留索引
const mergedSchema = contentSchema.clone();
mergedSchema.add(articleSchema);
const Article = mongoose.model('Article', mergedSchema);
多数据库继承
在跨数据库场景下,模型继承需要特殊处理。Mongoose允许通过connection选项指定不同的数据库连接。
多数据库继承示例:
const mainDB = mongoose.createConnection('mongodb://localhost/main');
const analyticsDB = mongoose.createConnection('mongodb://localhost/analytics');
const userSchema = new mongoose.Schema({
name: String,
email: String
});
// 主数据库用户模型
const User = mainDB.model('User', userSchema);
// 分析数据库继承用户模型
const AnalyticsUser = analyticsDB.model('User', userSchema);
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:模型(Model)的创建与使用
下一篇:创建文档(Create)