求助:如何用Node.js结合MongoDB基于多字段删除重复文档
多字段去重需求
我尝试用MongoDB聚合去重多次失败,转而用Node.js完成去重任务,目前已经实现了基于单个字段删除重复文档,但因为缺乏Node.js经验,无法完成基于多个字段的重复文档删除,希望能修改代码支持多字段去重。
现有代码
index.js
const MongoClient = require("mongodb").MongoClient; require("dotenv").config(); async function removeDups() { try { const client = await MongoClient.connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, }); const DB = client.db(process.env.DB_NAME); const coll = DB.collection(process.env.COLL_NAME); const tempColl = DB.collection(process.env.TEMP_COLL_NAME); await tempColl.createIndex({ [process.env.UNIQUE_FIELD]: 1 }, { unique: true }); const data = coll.find(); const total = await data.count(); let count = 0; let dupCount = 0; data.forEach(async (item) => { try { await tempColl.insertOne({ ...item }); count++; console.log("成功复制 " + count + " 条唯一文档。"); } catch (error) { // 捕获重复错误并忽略,其他错误抛出 dupCount++; } if (count + dupCount == total) { console.log("已插入 " + count + " 条唯一文档,移除了 " + dupCount + " 条重复文档。"); await DB.renameCollection(process.env.COLL_NAME,"temp_collection_review") await DB.renameCollection(process.env.TEMP_COLL_NAME,process.env.COLL_NAME) client.close(); process.exit(1); } }); } catch (error) { console.log(error); } } removeDups();
.env
DB_URL = "mongodb://127.0.0.1:27017" DB_NAME = test COLL_NAME = test UNIQUE_FIELD = user TEMP_COLL_NAME = products_temp
修改方案(支持多字段去重)
1. 更新 .env 配置
将单个字段配置改为逗号分隔的多字段列表,示例如下:
# 示例:以user和email两个字段的组合作为去重依据 UNIQUE_FIELDS = user,email DB_URL = "mongodb://127.0.0.1:27017" DB_NAME = test COLL_NAME = test TEMP_COLL_NAME = products_temp
2. 修改 index.js 代码
调整索引创建逻辑为复合唯一索引,同时修复原代码的异步遍历问题:
const MongoClient = require("mongodb").MongoClient; require("dotenv").config(); async function removeDups() { try { const client = await MongoClient.connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, }); const DB = client.db(process.env.DB_NAME); const coll = DB.collection(process.env.COLL_NAME); const tempColl = DB.collection(process.env.TEMP_COLL_NAME); // 将逗号分隔的字段字符串转为复合索引对象 const uniqueFields = process.env.UNIQUE_FIELDS.split(',').reduce((indexObj, field) => { indexObj[field.trim()] = 1; return indexObj; }, {}); // 创建复合唯一索引,MongoDB会根据字段组合判断重复 await tempColl.createIndex(uniqueFields, { unique: true }); const data = coll.find(); const total = await data.count(); let count = 0; let dupCount = 0; // 改用for await...of遍历,解决forEach异步导致的计数不准确问题 for await (const item of data) { try { await tempColl.insertOne({ ...item }); count++; console.log("成功复制 " + count + " 条唯一文档。"); } catch (error) { // 仅忽略重复键错误(错误码11000),其他错误正常抛出 if (error.code === 11000) { dupCount++; } else { throw error; } } } console.log("已插入 " + count + " 条唯一文档,移除了 " + dupCount + " 条重复文档。"); await DB.renameCollection(process.env.COLL_NAME, "temp_collection_review"); await DB.renameCollection(process.env.TEMP_COLL_NAME, process.env.COLL_NAME); client.close(); process.exit(0); } catch (error) { console.error("执行出错:", error); process.exit(1); } } removeDups();
关键修改说明
- 替换单字段配置
UNIQUE_FIELD为多字段配置UNIQUE_FIELDS,支持逗号分隔多个字段 - 创建复合唯一索引,MongoDB会基于多个字段的组合判断文档是否重复
- 用
for await...of替换forEach,解决原代码异步遍历导致的计数、流程判断不准确问题 - 增加错误类型判断,仅忽略重复键错误,其他错误正常抛出,便于排查问题
内容的提问来源于stack exchange,提问作者jerome
相关产品推荐
相关产品推荐

