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

MongoDB:基于行值匹配条件合并同一用户的关联多行数据

合并同一用户下的关联行(基于共同电话/邮箱)

我来帮你搞定这个合并需求~本质上这是个找连通分量的问题:把同一用户下,共享至少一个电话或邮箱的行看作互相连通的节点,最后把每个连通组的数据合并到一起。

核心思路拆解

  • 第一步:先按user字段分组,不同用户的数据完全独立处理,绝不交叉合并。
  • 第二步:对每个用户的数据集,构建节点连通关系:只要两行有共同的phone或email,就标记它们属于同一个连通组。
  • 第三步:对每个连通组做数据合并:
    • objectIds:收集组内所有行的objectId(存为数组)
    • phones:提取组内所有phone,去重后转数组
    • emails:提取组内所有email,去重后转数组
    • user:保留该组的用户ID

JavaScript代码实现(可直接运行)

// 你的示例输入数据
const rawData = [
  { objectId: 1, user: 1, phones: [1, 2], emails: ['a'] },
  { objectId: 2, user: 1, phones: [1, 5], emails: ['a', 'f'] },
  { objectId: 3, user: 1, phones: [8, 9], emails: ['f', 'g'] },
  { objectId: 4, user: 1, phones: [10], emails: ['h'] },
  { objectId: 5, user: 2, phones: [1, 2, 3], emails: ['aa', 'bb', 'cc'] }
];

// 1. 先按user分组,隔离不同用户的数据
const userGroups = {};
rawData.forEach(item => {
  if (!userGroups[item.user]) userGroups[item.user] = [];
  userGroups[item.user].push(item);
});

// 2. 对每个用户组,用并查集找连通分量并合并
const finalResult = [];
Object.values(userGroups).forEach(group => {
  // 构建phone/email到行索引的映射,快速找关联行
  const phoneToIndices = new Map();
  const emailToIndices = new Map();
  group.forEach((item, idx) => {
    item.phones.forEach(phone => {
      if (!phoneToIndices.has(phone)) phoneToIndices.set(phone, []);
      phoneToIndices.get(phone).push(idx);
    });
    item.emails.forEach(email => {
      if (!emailToIndices.has(email)) emailToIndices.set(email, []);
      emailToIndices.get(email).push(idx);
    });
  });

  // 并查集核心:找根节点 + 合并节点
  const parent = group.map((_, i) => i);
  const findRoot = (x) => {
    if (parent[x] !== x) parent[x] = findRoot(parent[x]);
    return parent[x];
  };
  const mergeNodes = (x, y) => {
    const rootX = findRoot(x);
    const rootY = findRoot(y);
    if (rootX !== rootY) parent[rootY] = rootX;
  };

  // 基于phone关联合并节点
  phoneToIndices.forEach(indices => {
    for (let i = 1; i < indices.length; i++) mergeNodes(indices[0], indices[i]);
  });
  // 基于email关联合并节点
  emailToIndices.forEach(indices => {
    for (let i = 1; i < indices.length; i++) mergeNodes(indices[0], indices[i]);
  });

  // 按根节点分组,合并每组数据
  const components = new Map();
  group.forEach((item, idx) => {
    const root = findRoot(idx);
    if (!components.has(root)) {
      components.set(root, {
        objectIds: [],
        phones: new Set(),
        emails: new Set(),
        user: item.user
      });
    }
    const comp = components.get(root);
    comp.objectIds.push(item.objectId);
    item.phones.forEach(p => comp.phones.add(p));
    item.emails.forEach(e => comp.emails.add(e));
  });

  // 转换为最终输出格式(Set转数组)
  Array.from(components.values()).forEach(comp => {
    finalResult.push({
      objectIds: comp.objectIds,
      user: comp.user,
      phones: Array.from(comp.phones),
      emails: Array.from(comp.emails)
    });
  });
});

// 打印结果
console.log(JSON.stringify(finalResult, null, 2));

运行结果展示

[
  {
    "objectIds": [1, 2, 3],
    "user": 1,
    "phones": [1, 2, 5, 8, 9],
    "emails": ["a", "f", "g"]
  },
  {
    "objectIds": [4],
    "user": 1,
    "phones": [10],
    "emails": ["h"]
  },
  {
    "objectIds": [5],
    "user": 2,
    "phones": [1, 2, 3],
    "emails": ["aa", "bb", "cc"]
  }
]

简单解释下:

  • user=1的行1、2、3是关联的:1和2共享phone=1/email=a,2和3共享email=f,所以三者合并成一组。
  • user=1的第4行没有和其他行共享任何phone/email,单独成组。
  • user=2只有一行,直接保留。

内容的提问来源于stack exchange,提问作者Trung Tín Trần

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:53:56