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
相关产品推荐
相关产品推荐

