You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

MongoDB聚合:未知邮箱字段名分组及unwind后完整文档数据获取求助

解决按未知邮箱字段分组并保留完整文档的问题

你的核心问题在于,原来的聚合流程拆分文档为键值对数组后,丢失了原始文档的引用——你只把拆分后的单个键值对推入了dups,而非完整的原始文档。下面是修正后的聚合管道,完美匹配你的需求:

db.test.aggregate([
  // 第一步:保留完整原始文档,同时把文档转为键值对数组
  {
    $project: {
      originalDoc: "$$ROOT",
      data: { $objectToArray: "$$ROOT" }
    }
  },
  // 第二步:拆分键值对数组,让每个键值对单独成为一条记录
  { $unwind: "$data" },
  // 第三步:过滤出值符合邮箱格式的键值对,只保留邮箱相关的字段
  {
    $match: {
      "data.v": /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/
    }
  },
  // 第四步:按邮箱值分组,把完整原始文档推入dups,并统计分组数量
  {
    $group: {
      _id: "$data.v", // 直接用邮箱值作为分组ID
      dups: { $push: "$originalDoc" },
      count: { $sum: 1 }
    }
  },
  // 可选:如果需要把邮箱字段名也展示出来,可以添加这个阶段
  {
    $addFields: {
      emailField: { $arrayElemAt: ["$dups.data.k", 0] }
    }
  }
])

关键修正逻辑说明:

  • 保留原始文档:第一个$project阶段用originalDoc: "$$ROOT"把整个原始文档保存下来,这是后续能在dups中拿到完整文档的核心。
  • 精准过滤邮箱字段:在$match阶段直接筛选值符合邮箱正则的键值对,避免无关字段干扰分组逻辑。
  • 分组时引用原始文档$group阶段的dups使用$push: "$originalDoc",确保每个分组里的dups都是完整的原始文档,而非单个键值对。

最终输出效果(完全匹配你的期望):

[
  {
    "_id": "abc@gmail.com",
    "count": 2,
    "dups": [
      { "_id": "62af2c14e50225b659ab68a4", "name": "pqr", "email": "abc@gmail.com", "phone": "9876543210" },
      { "_id": "62af2c14e50225b659ab68a6", "name": "abc", "email": "abc@gmail.com", "phone": "4567890123" }
    ]
  },
  {
    "_id": "xyz@gmail.in",
    "count": 2,
    "dups": [
      { "_id": "62af2c14e50225b659ab68a3", "name": "stuv", "email": "xyz@gmail.in", "phone": "12345678990" },
      { "_id": "62af2c14e50225b659ab68a5", "name": "xyz", "email": "xyz@gmail.in", "phone": "2345678901" }
    ]
  }
]

额外优化建议:

  • 如果你的数据库中存在多个邮箱字段(比如同时有gmailpersonalMail),这个管道会把同一个文档按不同邮箱字段分别分组。如果需要避免重复统计同一文档,可以在$match后添加一个$group阶段,按originalDoc._id去重。
  • 邮箱正则可以优化为更通用的版本,比如支持.co.uk这类多级域名:/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/

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

火山引擎 最新活动