如何在Mongoose中实现分类层级分组(不修改Schema)
Hey there! Since you don’t want to modify your existing Mongoose schema, we’ve got two solid approaches to turn those flat documents into the nested structure you need—no schema changes required.
Option 1: Build Nested Structure In-Memory (Great for Small Datasets)
This method fetches all flat category documents first, then rearranges them into the nested hierarchy using plain JavaScript. It’s straightforward and easy to tweak if your category levels change later.
const Category = require('./your-category-model-path'); // Replace with your actual model path async function getNestedCategories() { // Fetch only the fields we need, using lean() for plain JS objects (faster!) const flatCategories = await Category.find({}, '_id category parent_id').lean(); // Create a lookup map to quickly find categories by ID, plus initialize sub-arrays const categoryMap = {}; flatCategories.forEach(cat => { categoryMap[cat._id] = { ...cat, sub_cat1: [], sub_cat2: [] }; }); const nestedRoots = []; // Loop through each category and nest it under its parent flatCategories.forEach(cat => { const currentCat = categoryMap[cat._id]; if (cat.parent_id === null) { // This is a top-level category—add it to the root list nestedRoots.push(currentCat); } else { const parentCat = categoryMap[cat.parent_id]; if (parentCat) { // Determine which sub-category array to use based on parent's level if (parentCat.parent_id === null) { parentCat.sub_cat1.push(currentCat); } else { parentCat.sub_cat2.push(currentCat); } } } }); // Wrap the result in the required "Category" array return { Category: nestedRoots }; } // Example usage getNestedCategories() .then(result => console.log(JSON.stringify(result, null, 2))) .catch(err => console.error('Error building nested categories:', err));
Key Notes:
- Using
lean()skips Mongoose’s document wrapping, making object manipulation faster and simpler. - The lookup map ensures we don’t have to loop through the entire list every time we need to find a parent category.
Option 2: Use MongoDB Aggregation Pipeline (Better for Large Datasets)
If you’re working with a large number of categories, let the database handle the nesting with an aggregation pipeline. This reduces the amount of data transferred to your app and leverages MongoDB’s optimized query engine.
const Category = require('./your-category-model-path'); async function getNestedCategoriesWithAggregation() { const aggregationResult = await Category.aggregate([ // First, link each category to its direct children (sub_cat2 candidates) { $lookup: { from: 'categories', // Replace with your actual collection name (usually lowercase plural of model name) localField: '_id', foreignField: 'parent_id', as: 'sub_cat2' } }, // Next, link root categories to their direct children (sub_cat1 candidates) { $lookup: { from: 'categories', localField: '_id', foreignField: 'parent_id', as: 'sub_cat1' } }, // Filter to only keep top-level categories (parent_id is null) { $match: { parent_id: null } }, // Clean up the sub_cat1 structure to include their own sub_cat2 arrays { $project: { _id: 1, category: 1, sub_cat1: { $map: { input: '$sub_cat1', as: 'sub1', in: { _id: '$$sub1._id', category: '$$sub1.category', sub_cat2: '$$sub1.sub_cat2' } } } } }, // Wrap all root categories into the required "Category" array { $group: { _id: null, Category: { $push: '$$ROOT' } } }, // Remove the auto-generated _id field from the final result { $project: { _id: 0 } } ]); // Aggregation returns an array—we just need the first (and only) object return aggregationResult[0]; } // Example usage getNestedCategoriesWithAggregation() .then(result => console.log(JSON.stringify(result, null, 2))) .catch(err => console.error('Aggregation error:', err));
Key Notes:
- Double-check the
fromfield value—it should match your actual MongoDB collection name (Mongoose defaults to lowercase pluralized model names, e.g., "categories" for a model named "Category"). - This method is more efficient for large datasets because most of the processing happens on the database side.
内容的提问来源于stack exchange,提问作者Vishnu
相关产品推荐
相关产品推荐

