Mongoose 鉴别器(Discriminators)如何使用?
Mongoose Discriminators(鉴别器)是一种模式继承机制,允许你在同一个集合中存储不同类型的文档,同时保持各自独特的字段和验证规则。这对于处理具有共同基础但又有特定差异的数据模型非常有用。基本概念创建基础 Schemaconst eventSchema = new Schema({ name: { type: String, required: true }, date: { type: Date, required: true }, location: String}, { discriminatorKey: 'kind' // 用于区分不同类型的字段});const Event = mongoose.model('Event', eventSchema);创建鉴别器// 创建会议类型的鉴别器const conferenceSchema = new Schema({ speakers: [String], sponsors: [String]});const Conference = Event.discriminator('Conference', conferenceSchema);// 创建聚会类型的鉴别器const meetupSchema = new Schema({ attendees: Number, maxAttendees: Number});const Meetup = Event.discriminator('Meetup', meetupSchema);使用鉴别器创建文档// 创建基础事件const event = await Event.create({ name: 'General Event', date: new Date('2024-01-01'), location: 'New York'});// 创建会议const conference = await Conference.create({ name: 'Tech Conference', date: new Date('2024-02-01'), location: 'San Francisco', speakers: ['Alice', 'Bob'], sponsors: ['Company A', 'Company B']});// 创建聚会const meetup = await Meetup.create({ name: 'Developer Meetup', date: new Date('2024-03-01'), location: 'Boston', attendees: 50, maxAttendees: 100});查询文档// 查询所有事件const allEvents = await Event.find();// 查询特定类型的事件const conferences = await Conference.find();const meetups = await Meetup.find();// 使用 discriminatorKey 查询const conferences2 = await Event.find({ kind: 'Conference' });嵌套鉴别器在子文档中使用鉴别器const batchSchema = new Schema({ name: String, size: Number, product: { type: Schema.Types.ObjectId, ref: 'Product' }}, { discriminatorKey: 'kind'});const orderSchema = new Schema({ customer: String, items: [batchSchema]});// 创建产品类型的鉴别器const productBatchSchema = new Schema({ quantity: Number, unit: String});const productBatch = batchSchema.discriminator('ProductBatch', productBatchSchema);// 创建服务类型的鉴别器const serviceBatchSchema = new Schema({ duration: Number, rate: Number});const serviceBatch = batchSchema.discriminator('ServiceBatch', serviceBatchSchema);鉴别器中间件为鉴别器添加中间件// 为会议添加中间件conferenceSchema.pre('save', function(next) { console.log('Saving conference:', this.name); next();});// 为聚会添加中间件meetupSchema.pre('save', function(next) { if (this.attendees > this.maxAttendees) { return next(new Error('Attendees cannot exceed max')); } next();});基础 Schema 的中间件// 基础 Schema 的中间件会应用到所有鉴别器eventSchema.pre('save', function(next) { console.log('Saving event:', this.name); next();});鉴别器方法为鉴别器添加方法// 为会议添加方法conferenceSchema.methods.getSpeakerCount = function() { return this.speakers.length;};// 为聚会添加方法meetupSchema.methods.getAvailableSpots = function() { return this.maxAttendees - this.attendees;};// 使用方法const conference = await Conference.findById(conferenceId);console.log(conference.getSpeakerCount());const meetup = await Meetup.findById(meetupId);console.log(meetup.getAvailableSpots());鉴别器验证为鉴别器添加验证conferenceSchema.path('speakers').validate(function(speakers) { return speakers.length > 0;}, 'Conference must have at least one speaker');meetupSchema.path('attendees').validate(function(attendees) { return attendees >= 0;}, 'Attendees cannot be negative');实际应用场景1. 内容管理系统const contentSchema = new Schema({ title: { type: String, required: true }, author: { type: String, required: true }, publishedAt: Date}, { discriminatorKey: 'contentType'});const Content = mongoose.model('Content', contentSchema);// 文章类型const articleSchema = new Schema({ body: String, tags: [String]});const Article = Content.discriminator('Article', articleSchema);// 视频类型const videoSchema = new Schema({ url: String, duration: Number, thumbnail: String});const Video = Content.discriminator('Video', videoSchema);2. 用户角色系统const userSchema = new Schema({ name: { type: String, required: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true }}, { discriminatorKey: 'role'});const User = mongoose.model('User', userSchema);// 管理员const adminSchema = new Schema({ permissions: [String], department: String});const Admin = User.discriminator('Admin', adminSchema);// 客户const customerSchema = new Schema({ address: String, phone: String, loyaltyPoints: { type: Number, default: 0 }});const Customer = User.discriminator('Customer', customerSchema);3. 订单系统const orderSchema = new Schema({ orderNumber: { type: String, required: true }, customer: { type: Schema.Types.ObjectId, ref: 'User' }, total: Number, status: String}, { discriminatorKey: 'orderType'});const Order = mongoose.model('Order', orderSchema);// 在线订单const onlineOrderSchema = new Schema({ shippingAddress: String, trackingNumber: String});const OnlineOrder = Order.discriminator('OnlineOrder', onlineOrderSchema);// 到店订单const inStoreOrderSchema = new Schema({ pickupTime: Date, storeLocation: String});const InStoreOrder = Order.discriminator('InStoreOrder', inStoreOrderSchema);鉴别器 vs 嵌入文档选择指南使用鉴别器当:需要在同一个集合中查询所有类型不同类型有大量共同字段需要统一的索引和查询类型数量相对较少使用嵌入文档当:每种类型有完全不同的结构不需要跨类型查询需要更好的性能隔离类型数量很多最佳实践合理设计基础 Schema:基础 Schema 应包含所有类型的共同字段使用清晰的 discriminatorKey:选择有意义的字段名来区分类型为鉴别器添加验证:确保每种类型的数据完整性利用中间件:为不同类型添加特定的业务逻辑考虑性能:鉴别器在同一个集合中,可能影响查询性能文档清晰:为每个鉴别器添加清晰的注释测试覆盖:为每种鉴别器编写测试