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

如何在MongoDB中查询子数组并保留成员role字段?

MongoDB聚合查询:保留members数组中的role字段

问题背景

现有groups和users两个集合,数据如下:

groups集合数据

db.groups.insertMany([
    {
        title: 'Hi',
        members: [
            {
                userId: ObjectId('62589515f239750e7e44b958'), 
                role: 'Admin'
            },
            {
                userId: ObjectId('655f634c632d0c23b9d455de'), 
                role: 'Member'
            }
        ]
    }
])

users集合数据

db.users.insertMany([
    {
        _id: ObjectId('62589515f239750e7e44b958'), 
        name: 'A',
    },
    {
        _id: ObjectId('655f634c632d0c23b9d455de'), 
        name: 'B',
    }
])

需要通过聚合操作得到如下格式的结果:

{
    title: 'Hi',
    members: [
        {
            user: {
                _id: ObjectId('62589515f239750e7e44b958'), 
                name: 'A',
            },
            role: 'Admin'
        },
        {
            user: {
                _id: ObjectId('655f634c632d0c23b9d455de'), 
                name: 'B',
            },
            role: 'Member'
        },
    ]
}

解决方案

提供两种可行的聚合管道实现方案,按需选择:

方案一:拆分-关联-重组数组

db.groups.aggregate([
    // 拆分members数组,保留每个元素的role和userId
    { $unwind: "$members" },
    // 关联users集合,匹配用户信息
    {
        $lookup: {
            from: "users",
            localField: "members.userId",
            foreignField: "_id",
            as: "memberUser"
        }
    },
    // 将关联到的用户数据转为单个对象,同时保留role字段
    {
        $addFields: {
            "members.user": { $arrayElemAt: ["$memberUser", 0] },
            "members.role": "$members.role"
        }
    },
    // 清理临时字段
    { $unset: "memberUser" },
    // 按title分组,重组members数组
    {
        $group: {
            _id: "$title",
            title: { $first: "$title" },
            members: { $push: "$members" }
        }
    },
    // 移除分组生成的_id字段(可选)
    { $unset: "_id" }
])

步骤说明:

  • $unwind:把members数组拆分为单个文档,每个文档包含原分组信息和一个带userId、role的成员项。
  • $lookup:根据members.userId关联users集合,匹配结果存入memberUser数组。
  • $addFields:将memberUser数组的唯一元素赋值给members.user,同时显式保留原role字段。
  • $unset:删除临时生成的memberUser字段。
  • $group:按title分组,把拆分后的成员项重新组合成数组。
  • $unset:可选操作,移除分组产生的_id,让结果结构更贴合需求。

方案二:直接关联数组内元素(MongoDB 3.6+支持)

db.groups.aggregate([
    {
        $lookup: {
            from: "users",
            let: { members: "$members" },
            pipeline: [
                {
                    $match: {
                        $expr: { $in: ["$_id", "$$members.userId"] }
                    }
                },
                {
                    $project: {
                        name: 1,
                        userId: "$_id"
                    }
                }
            ],
            as: "usersList"
        }
    },
    {
        $addFields: {
            members: {
                $map: {
                    input: "$members",
                    as: "m",
                    in: {
                        role: "$$m.role",
                        user: {
                            $arrayElemAt: [
                                {
                                    $filter: {
                                        input: "$usersList",
                                        as: "u",
                                        cond: { $eq: ["$$u.userId", "$$m.userId"] }
                                    }
                                },
                                0
                            ]
                        }
                    }
                }
            }
        }
    },
    { $unset: "usersList" }
])

步骤说明:

  • $lookup:通过let传递当前文档的members数组,子管道匹配所有_id在members.userId中的用户,并投影出所需字段。
  • $addFields + $map:遍历原members数组,对每个成员项,用$filter从usersList中匹配对应用户,将其作为user字段,同时保留原role。
  • $unset:删除临时的usersList字段。

两种方案均可得到预期结果,方案二无需拆分数组,在大数据量场景下性能更优。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 08:43:31