Node.js使用MongoDB insertMany出现重复文档,如何按Nazwisko字段去重插入
问题根因
fs.watch原生存在单次文件修改触发多次change事件的特性,是操作系统层面的文件写入操作分多步触发导致的- 现有代码未处理异步逻辑,
insertMany是异步方法,原有try/catch无法捕获异常,且每次触发事件都重复建立MongoDB连接,容易出现并发写入异常 - 缺少重复数据校验逻辑,未从数据库层面或业务逻辑层限制
Nazwisko字段重复
修复方案
1. 先给集合创建唯一索引(推荐,性能最高)
直接在MongoDB shell执行以下命令,从数据库层面保证Nazwisko字段唯一,重复数据写入时会自动抛出11000重复键错误:
db.candidate.createIndex({ Nazwisko: 1 }, { unique: true })
2. 优化后的Node.js代码
const fs = require('fs') const { MongoClient } = require('mongodb') // 初始化Mongo连接,复用连接不要每次请求都新建 const client = new MongoClient('你的MongoDB连接串') let db = null // 连接初始化只执行一次 client.connect().then(() => { db = client.db('test') console.log('MongoDB连接初始化成功') }).catch(err => { console.log('MongoDB连接失败:', err) }) // 防抖函数,300ms内多次触发只执行最后一次,可根据实际文件大小调整时长 function debounce(fn, delay = 300) { let timer = null return function(...args) { clearTimeout(timer) timer = setTimeout(() => fn.apply(this, args), delay) } } // 监听逻辑加防抖包装 fs.watch(file("Lista.json"), debounce(async function (eventType, filename) { if (!filename || !db) return // 过滤无效触发和连接未就绪的情况 try { // 读取解析文件 const data = await fs.promises.readFile(file(filename)) const bazaJsonObject = JSON.parse(data.toString()) const candidate = db.collection("candidate") // ordered:false 表示某条写入失败不中断其他条目写入 await candidate.insertMany(bazaJsonObject, { ordered: false }) console.log('数据写入完成') } catch (e) { // 11000是重复键错误码,属于正常的重复数据过滤,不用抛出异常 if (e.code !== 11000) { console.log('写入异常:', e) } } }))
备选方案:不建唯一索引的业务层去重
如果不方便修改集合索引,可以用bulkWrite实现存在则跳过的逻辑,替换上面代码中的insertMany部分即可:
const operations = bazaJsonObject.map(item => ({ updateOne: { filter: { Nazwisko: item.Nazwisko }, update: { $setOnInsert: item }, // 只有插入时才写入数据,已存在则不做修改 upsert: true } })) await candidate.bulkWrite(operations, { ordered: false })
注意:如果有多个不同条目Nazwisko相同的情况,两种方案都会只保留第一条数据,符合需求
内容的提问来源于stack exchange,提问作者bialka104b
相关产品推荐
相关产品推荐

