如何统计分类下文章数量?Mongoose实现方案咨询
实现分类文章数量统计的两种方案
一、你的现有方案:维护冗余字段(posts_Number)
合理性分析
这种方案的优点是查询分类列表时性能高,直接读取字段即可展示;但缺点也很明显:
- 需要在文章创建、删除、修改分类时同步更新
posts_Number,一旦某个环节出错(比如创建文章失败但分类计数已增加),就会出现数据不一致; - 如果分类名称修改,文章里的
categories字段(当前是字符串)无法自动同步,会导致计数统计错误。
代码实现(修改创建文章的路由)
要实现创建文章时分类计数自增,需在文章保存成功后,找到对应分类并更新posts_Number,建议用Mongoose的findOneAndUpdate做原子操作,同时处理异常:
// CREATE POST router.post("/", async (req, res) => { try { // 1. 创建并保存文章 const newPost = new Post(req.body); const savedPost = await newPost.save(); // 2. 如果文章指定了分类,更新对应分类的文章数 if (savedPost.categories) { // 按用户名+分类名匹配,避免跨用户混淆 await PostsCategory.findOneAndUpdate( { name: savedPost.categories, username: savedPost.username }, { $inc: { posts_Number: 1 } }, // 原子自增操作 { upsert: false, new: true } // upsert设为true则分类不存在时自动创建,按需调整 ); } res.status(200).json(savedPost); } catch (err) { res.status(500).json(err); console.log(err); } });
注意:后续如果有文章删除、修改分类的操作,也要同步更新对应分类的
posts_Number——比如删除文章时减1,修改分类时给旧分类减1、新分类加1。
二、更健壮的方案:实时统计(不维护冗余字段)
方案思路
放弃维护posts_Number字段,在查询分类列表时,通过MongoDB的聚合查询实时统计每个分类对应的文章数量。这种方案彻底避免数据不一致问题,适合数据量不是特别大的后台管理系统。
优化模型关联(可选但推荐)
建议把Post模型的categories字段从字符串改为ObjectId,关联到PostsCategory的_id,这样即使分类名称修改,关联关系也不会断裂:
// 修改后的Post模型 const PostSchema = new mongoose.Schema( { username: { type: String, required: true, }, title: { type: String, required: true, }, categories: { type: mongoose.Schema.Types.ObjectId, ref: "PostsCategory", // 关联PostsCategory模型 required: false, }, }, { timestamps: true } );
分类列表查询的聚合代码
在获取分类列表的路由中,用aggregate统计每个分类的文章数:
// 获取带文章数量的分类列表 router.get("/categories", async (req, res) => { try { const { username } = req.query; // 按用户名过滤,避免跨用户查询 const categoriesWithCount = await PostsCategory.aggregate([ // 1. 过滤当前用户的分类 { $match: { username } }, // 2. 关联文章集合,匹配分类ID { $lookup: { from: "posts", // 注意这里是Post模型对应的集合名称(一般为小写复数) localField: "_id", foreignField: "categories", as: "postsList", }, }, // 3. 统计文章数量,保留分类其他字段 { $project: { username: 1, name: 1, catImage: 1, createdAt: 1, updatedAt: 1, posts_Number: { $size: "$postsList" }, // 统计关联的文章数组长度 }, }, ]); res.status(200).json(categoriesWithCount); } catch (err) { res.status(500).json(err); console.log(err); } });
如果不修改模型,仍用字符串匹配分类名称,聚合查询可改为:
router.get("/categories", async (req, res) => { try { const { username } = req.query; const categoriesWithCount = await PostsCategory.aggregate([ { $match: { username } }, { $lookup: { from: "posts", let: { catName: "$name", user: "$username" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$categories", "$$catName"] }, { $eq: ["$username", "$$user"] }, ], }, }, }, ], as: "postsList", }, }, { $project: { username: 1, name: 1, catImage: 1, createdAt: 1, updatedAt: 1, posts_Number: { $size: "$postsList" }, }, }, ]); res.status(200).json(categoriesWithCount); } catch (err) { res.status(500).json(err); console.log(err); } });
方案选择建议
- 如果系统文章和分类数量极大、对查询性能要求极高,选第一种方案,但要确保所有修改文章分类的操作都同步更新计数;
- 大多数后台管理系统数据量不大,推荐第二种方案,数据一致性更有保障,代码维护更简单。
内容的提问来源于stack exchange,提问作者sultan.h
相关产品推荐
相关产品推荐

