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

MongoDB聚合实现同集合父子分类关联填充parentCategory

问题解决思路

你之前的聚合逻辑存在三个核心问题,导致无法得到预期结构:

  • 第一次$lookup关联categories集合得到的是数组结果,没有转换为单个文档对象,后续字段路径取值错误
  • 拿到当前分类的parentCategory父分类ID列表后,没有二次关联拉取父分类完整信息,反而用多余的$project/$group阶段破坏了原有正确的字段结构
  • 你存储的parentCategory是字符串类型的ID,关联_id(ObjectId类型)时没有做类型转换,会出现匹配不到数据的问题
正确实现代码

直接替换你原有聚合管道的内容即可,核心调整是补全父分类关联逻辑、修正字段结构:

export const categoriesAtHome = async (req: Request & any, res: Response, next: NextFunction) => {
    try {
        const { affiliateId } = req.params;
        const affiliateObjectId = Types.ObjectId(affiliateId);

        const categoryConditions = {
            enabled: true,
            active: true
        };

        const data = await ServicesByAffiliate.aggregate([
            // 1. 匹配关联关系表有效数据
            {
                $match: { affiliate: affiliateObjectId, enabled: true }
            },
            // 2. 关联服务信息
            {
                $lookup: {
                    from: "services",
                    localField: "service",
                    foreignField: "_id",
                    as: "service"
                }
            },
            {
                $unwind: "$service"
            },
            // 3. 关联当前分类信息,把数组结果转为单个对象
            {
                $lookup: {
                    from: "categories",
                    localField: "category",
                    foreignField: "_id",
                    as: "category",
                }
            },
            {
                $addFields: {
                    // lookup返回数组,取第一个元素作为当前分类对象
                    category: { $arrayElemAt: ["$category", 0] }
                }
            },
            // 4. 关联父分类完整信息
            {
                $lookup: {
                    from: "categories",
                    let: {
                        // 转换父分类ID为ObjectId类型匹配外键
                        parentIds: {
                            $map: {
                                input: "$category.parentCategory",
                                as: "pid",
                                in: { $toObjectId: "$$pid" }
                            }
                        }
                    },
                    pipeline: [
                        {
                            $match: {
                                $expr: { $in: ["$_id", "$$parentIds"] },
                                ...categoryConditions
                            }
                        }
                    ],
                    as: "category.parentCategory"
                }
            },
            {
                $addFields: {
                    // 父分类lookup结果是数组,按需求取第一个转为单个对象,无父分类则为null
                    "category.parentCategory": { $ifNull: [{ $arrayElemAt: ["$category.parentCategory", 0] }, null] }
                }
            }
        ]);

        res.status(200).json({
            data: data
        });

    } catch (error) {
        res.status(500).json({ error: true, message: error.toString() });
    }
};
补充说明
  • 如果业务场景中一个分类存在多个父分类,删掉最后一个$addFields阶段即可,parentCategory会保留为父分类对象数组
  • 如果parentCategory字段存储的本身就是ObjectId类型,删掉$map里的$toObjectId转换逻辑即可
  • 该场景只需要一层父分类关联,不需要用$graphLookup,普通$lookup性能更好

内容的提问来源于stack exchange,提问作者Izlia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 01:15:39