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

如何用MongoDB聚合管道实现键值互换并整合邮箱与手机号

如何通过MongoDB聚合直接生成键为邮箱/手机号的结果

集合文档结构

现有MongoDB集合包含以下文档:

{ 
    "_id" : ObjectId("65977bef1ccf990fe33ffc0d"), 
    "email" : "yww24@gmail.com"
}
{ 
    "_id" : ObjectId("65977bef1ccf990fe33ffc0f"), 
    "email" : "iqiyz@gmail.com", 
    "phone" : "8887774455"
}
{ 
    "_id" : ObjectId("65977bef1ccf990fe33ffc10"), 
    "email" : "46d9s@gmail.com"
}
{ 
    "_id" : ObjectId("65977bef1ccf990fe33ffc0e"), 
    "phone" : "6669997744"
}

期望结果

希望通过聚合操作直接得到以邮箱、手机号为键的对象,值可以是任意标识(如序号、true等),示例如下:

{
  "yww24@gmail.com": ["0"],
  "iqiyz@gmail.com": ["1"],
  "46d9s@gmail.com": ["2"],
  "8887774455": ["1"],
  "6669997744": ["3"]
}

现有前端实现(Lodash方式)

当前可通过查询后用Lodash处理得到结果,但希望直接在数据库端完成聚合:

Coll.aggregate([
    { $match: {} },
    {
        $project: {
            _id: false,
            email: true,
            phone: true
        }
    }
])
.then(logs => {
    const result = {
        emails: _.invertBy(logs, 'email'),
        phones: _.invertBy(logs, 'phone')
    };
    delete result.emails.undefined;
    delete result.phones.undefined;
    console.log({...result.emails, ...result.phones});
})

MongoDB聚合解决方案

以下聚合管道可直接在数据库端生成目标格式:

db.collection.aggregate([
    // 给每个文档分配递增序号
    {
        $setWindowFields: {
            sortBy: { _id: 1 },
            output: {
                docIndex: { $documentNumber: {} }
            }
        }
    },
    // 将email/phone转为统一的键值结构,过滤空值
    {
        $project: {
            entries: {
                $filter: {
                    input: [
                        { k: "$email", v: [ { $toString: "$docIndex" } ] },
                        { k: "$phone", v: [ { $toString: "$docIndex" } ] }
                    ],
                    cond: { $ne: ["$$this.k", null] }
                }
            }
        }
    },
    // 拆分数组为单个文档
    { $unwind: "$entries" },
    // 收集所有键值对
    {
        $group: {
            _id: null,
            allEntries: { $push: "$entries" }
        }
    },
    // 将数组转为目标对象
    {
        $replaceRoot: {
            newRoot: { $arrayToObject: "$allEntries" }
        }
    }
])

管道说明

  1. $setWindowFields:按_id排序为每个文档分配唯一递增序号,作为值的来源;
  2. $project:把email和phone转为{k: 键名, v: [序号字符串]}的结构,用$filter过滤掉不存在的字段(值为null的情况);
  3. $unwind:将每个文档的entries数组拆分为单独文档,方便后续聚合;
  4. $group:把所有键值对收集到一个数组中;
  5. $replaceRoot:通过$arrayToObject将数组转换为目标格式的对象,替换根文档。

执行后将直接输出符合要求的结果,无需前端额外处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:16:06