基于Node.js、Apollo Server v4等构建分类树的输出问题咨询
问题分析与解决方案
1. 分类重复出现(既作为子分类又单独列出)
问题原因
查询时未过滤顶级分类,直接返回所有分类后手动构建树,导致子分类同时作为独立节点和嵌套节点存在。
修复方案
只查询顶级分类(parent 为 null),再通过递归关联子分类,避免重复返回:
// Resolver 修改 async getCategories() { // 递归填充子分类的函数 const populateChildren = () => ({ path: 'children', populate: populateChildren() }); // 仅查询顶级分类,并递归填充子分类 return await Category.find({ parent: null }) .populate(populateChildren()) .lean(); // 使用lean()提升查询性能,返回普通JS对象 }
同时给Mongoose Schema添加虚拟属性children,自动关联子分类:
// Category Mongoose Schema const categorySchema = new mongoose.Schema({ // ... 其他字段 parent: { type: mongoose.Schema.Types.ObjectId, ref: 'Category', default: null } }); // 定义虚拟属性children,关联子分类 categorySchema.virtual('children', { ref: 'Category', localField: '_id', foreignField: 'parent' }); // 确保虚拟属性在转换为对象/JSON时被包含 categorySchema.set('toObject', { virtuals: true }); categorySchema.set('toJSON', { virtuals: true });
2. 子分类意外关联且名称为空
问题原因
- 创建分类时未验证
parentID的合法性,导致无效ID被关联; - Mongoose Schema未强制
name字段必填,出现空名称的脏数据。
修复方案
- 给
name字段添加必填验证,给parent字段添加存在性验证:
// Category Mongoose Schema 修改 const categorySchema = new mongoose.Schema({ name: { type: String, required: [true, '分类名称不能为空'], trim: true }, parent: { type: mongoose.Schema.Types.ObjectId, ref: 'Category', default: null, validate: { validator: async function(v) { if (!v) return true; // 验证父分类ID是否存在 const parentCategory = await Category.findById(v); return !!parentCategory; }, message: '父分类不存在' } } // ... 其他字段 });
- 清理数据库中已存在的脏数据:删除ID为
655b105543598f2ef0ba0f69的空名称分类,或修正其关联关系。
3. attributes 返回null
问题原因
Mongoose Schema未给attributes设置默认值,新建分类时未赋值会导致字段为null,与GraphQL预期的数组类型不匹配。
修复方案
给attributes设置默认空数组:
// Category Mongoose Schema 修改 const categorySchema = new mongoose.Schema({ // ... 其他字段 attributes: { type: [{ type: String, trim: true }], default: [] // 默认空数组,避免返回null } });
同时调整GraphQL Schema的attributes类型,确保匹配:
type Category { # ... 其他字段 attributes: [String!]! # 明确为非空字符串数组 }
代码优化总结
- 利用Mongoose虚拟属性简化子分类关联,避免手动构建树的冗余代码;
- 添加强制性数据验证,从源头避免脏数据;
- 使用
lean()提升查询性能,减少Mongoose文档转换开销; - 统一数据库字段默认值与GraphQL类型定义,避免类型不匹配问题。
内容的提问来源于stack exchange,提问作者Knâkles
相关产品推荐
相关产品推荐

