You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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 from field 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:55:19