如何设计Mongoose商品Schema的分类模块 支持电子、时尚、食品等类目
Mongoose 多类目商品分类模块Schema搭建方案
核心设计思路
采用「公共字段统一存储 + 类目特有字段动态适配」的方案,不需要为每个类目单独建表,同时支持多级分类关联、类目属性合规校验,可灵活扩展任意商品类目。
1. 先搭建分类(Category)模型
用来存储类目层级、每个类目对应的特有属性模板,示例结构如下图:
对应Mongoose Schema代码:
const mongoose = require('mongoose'); const { Schema } = mongoose; const categorySchema = new Schema({ // 分类名称:电子/时尚/食品等一级分类,或手机/笔记本/女装等二级分类 name: { type: String, required: true, trim: true }, // 父分类ID,一级分类的parentId为null parentId: { type: Schema.Types.ObjectId, ref: 'Category', default: null }, // 分类层级:1为一级分类,2为二级分类,以此类推 level: { type: Number, required: true, min: 1 }, // 当前分类的特有属性模板,添加商品时会按照这个模板校验字段 attributeTemplate: [ { // 属性名:比如内存/尺码/保质期 attrName: { type: String, required: true }, // 属性值类型:String/Number/Boolean/Array attrType: { type: String, required: true, enum: ['String', 'Number', 'Boolean', 'Array'] }, // 是否为必填属性 required: { type: Boolean, default: false }, // 可选值枚举,比如尺码的可选值为['S','M','L','XL'] enumValues: { type: Array, default: [] } } ], // 分类状态:是否启用 isActive: { type: Boolean, default: true } }, { timestamps: true }); module.exports = mongoose.model('Category', categorySchema);
2. 搭建商品(Product)主模型
所有类目商品公共字段统一存储,特有属性存入dynamicAttributes字段,关联分类ID实现类目适配:
const productSchema = new Schema({ // 所有类目通用公共字段 name: { type: String, required: true, trim: true }, price: { type: Number, required: true, min: 0 }, mainImage: { type: String, required: true }, stock: { type: Number, required: true, min: 0 }, description: { type: String, default: '' }, // 关联分类ID,绑定对应类目 categoryId: { type: Schema.Types.ObjectId, ref: 'Category', required: true }, // 类目特有属性,和关联分类的attributeTemplate对应 dynamicAttributes: { type: Schema.Types.Mixed, default: {} }, // 商品上下架状态 isOnSale: { type: Boolean, default: true } }, { timestamps: true }); // 预保存中间件:新增/修改商品时,校验动态属性是否符合对应分类的模板要求 productSchema.pre('save', async function(next) { const category = await mongoose.model('Category').findById(this.categoryId); if (!category) throw new Error('关联分类不存在'); const template = category.attributeTemplate; const attrs = this.dynamicAttributes; // 校验必填属性是否存在 template.forEach(attr => { if (attr.required && attrs[attr.attrName] === undefined) { throw new Error(`缺少必填属性:${attr.attrName}`); } // 校验属性值类型 if (attrs[attr.attrName] !== undefined && typeof attrs[attr.attrName] !== attr.attrType.toLowerCase()) { throw new Error(`属性${attr.attrName}类型错误,应为${attr.attrType}`); } // 校验枚举值 if (attr.enumValues.length > 0 && !attr.enumValues.includes(attrs[attr.attrName])) { throw new Error(`属性${attr.attrName}的值不在可选范围内`); } }); next(); }); module.exports = mongoose.model('Product', productSchema);
使用示例
新增电子类(手机)商品
// 假设已存在手机分类,属性模板包含「内存、CPU、屏幕尺寸」三个属性 const newPhone = new Product({ name: 'XX品牌智能手机', price: 3999, mainImage: 'xxx.jpg', stock: 100, categoryId: 'xxxxxx(手机分类的ObjectId)', dynamicAttributes: { 内存: '8GB', CPU: '骁龙8 Gen2', 屏幕尺寸: '6.7英寸' } }); await newPhone.save();
新增时尚类(T恤)商品
// 假设已存在T恤分类,属性模板包含「尺码、面料、颜色」三个属性 const newTshirt = new Product({ name: 'XX品牌纯棉T恤', price: 99, mainImage: 'xxx.jpg', stock: 500, categoryId: 'xxxxxx(T恤分类的ObjectId)', dynamicAttributes: { 尺码: 'M', 面料: '100%棉', 颜色: '白色' } }); await newTshirt.save();
扩展优化建议
- 如果需要支持多条件筛选特有属性,可以给
dynamicAttributes下的常用字段加索引 - 可以新增分类参数组的功能,把同类目的通用属性模板批量复用,减少重复配置
内容的提问来源于stack exchange,提问作者Deepak
相关产品推荐
相关产品推荐

