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

如何在MongoDB中将对象数组与ObjectId数组合并为唯一列表

MongoDB聚合:合并数组并去重(含字段不存在处理)

问题场景

现有如下文档:

// 文档1
{
    _id: ObjectId('232be4e2fd568670ec11d98b'),
    name: 'Discord',
    type: 'A',
    devices: [
        ObjectId('642be505fd568670ec11d10c'),
        ObjectId('552be45cfd568670ec30d779')
    ],
    endpoints: [
        { refId: ObjectId('641316fd596514e63a2121ca') },
        { refId: ObjectId('642bd725c7e5ec334ee17ce7') },
        { refId: ObjectId('552be45cfd568670ec30d779') }
    ]
}

// 文档2
{
    _id: ObjectId('642c073643aaf70eee741e05'),
    name: 'Twitter',
    type: 'A',
    devices: [
        ObjectId('642c072b37e4e956dfed39a0'),
        ObjectId('642c0730aa06dc1f7906b9a3')
    ]
}

// 文档3
{
    _id: ObjectId('642c07b2fb3917c094e30734'),
    name: 'Facebook',
    type: 'A'
}

需要转换为仅包含_id和去重devices数组的文档,devices需合并原devices数组与endpoints.refId数组,且需处理字段不存在的情况,最终结果如下:

// 结果1
{
    _id: ObjectId('232be4e2fd568670ec11d98b'),
    devices: [
        ObjectId('642be505fd568670ec11d10c'),
        ObjectId('552be45cfd568670ec30d779'),
        ObjectId('641316fd596514e63a2121ca'),
        ObjectId('642bd725c7e5ec334ee17ce7')
    ]
}

// 结果2
{
    _id: ObjectId('642c073643aaf70eee741e05'),
    devices: [
        ObjectId('642c072b37e4e956dfed39a0'),
        ObjectId('642c0730aa06dc1f7906b9a3')
    ]
}

// 结果3
{
    _id: ObjectId('642c07b2fb3917c094e30734'),
    devices: []
}

原代码问题

你之前尝试的$group写法存在两个问题:

  1. 当endpoints不存在时,$addToSet: '$endpoints.refId'会产生null值,无法正确生成数组
  2. $push: '$devices'会把整个devices数组作为单个元素存入,而非合并数组内的元素

正确聚合管道(强制使用$group)

以下是满足需求的完整聚合管道,每一步都处理了字段不存在的场景:

db.collection.aggregate([
    // 步骤1:统一字段格式,处理不存在的情况
    {
        $addFields: {
            // 确保devices始终是数组,不存在则设为空数组
            devices: { $ifNull: ['$devices', []] },
            // 提取endpoints中的refId,不存在endpoints则设为空数组
            endpointRefs: {
                $map: {
                    input: { $ifNull: ['$endpoints', []] },
                    as: 'ep',
                    in: '$$ep.refId'
                }
            }
        }
    },
    // 步骤2:合并devices和endpointRefs为一个数组
    {
        $project: {
            combined: { $concatArrays: ['$devices', '$endpointRefs'] }
        }
    },
    // 步骤3:拆分合并后的数组,为去重做准备
    {
        $unwind: {
            path: '$combined',
            preserveNullAndEmptyArrays: true // 处理空数组的情况
        }
    },
    // 步骤4:按_id分组,用$addToSet去重收集元素
    {
        $group: {
            _id: '$_id',
            devices: { $addToSet: '$combined' }
        }
    },
    // 步骤5:确保devices为空数组时显示正确(可选)
    {
        $project: {
            _id: 1,
            devices: { $ifNull: ['$devices', []] }
        }
    }
])

步骤解释

  1. $addFields:将devices和endpoints统一为数组格式,避免字段不存在导致的错误;同时把endpoints中的refId提取成独立数组endpointRefs
  2. $project:用$concatArrays合并devices和endpointRefs,得到包含所有待去重元素的数组combined
  3. $unwind:拆分combined数组的每个元素,开启preserveNullAndEmptyArrays确保空数组的文档不会被过滤
  4. $group:按_id分组,用$addToSet自动去重并收集所有元素,生成最终的devices数组
  5. $project(可选):确保即使没有任何元素,devices也显示为空数组而非缺失

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 01:22:08