阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 模型的继承与扩展

模型的继承与扩展

作者:陈川 阅读数:24863人阅读 分类: MongoDB

模型继承的基本概念

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌