使用bulkWrite+updateOne设upsert=true仍创建新文档的问题
解决MongoDB bulkWrite upsert重复创建文档的问题
场景还原
从Google Calendar API获取节假日数据后,执行以下批量更新/插入代码,但开启upsert=true后仍会重复创建文档:
Holiday.bulkWrite( holidays.map((holiday) => ({ updateOne: { filter: { holidayId: holiday.id }, update: { $set: holiday }, upsert: true, }, })) )
问题原因及解决办法
1. 统一holiday.id的格式一致性
如果每次获取的holiday.id存在大小写差异、前后空格或数据类型不匹配,会导致MongoDB无法匹配到已有文档,触发upsert创建新记录。
解决: 标准化处理holiday.id格式,同步更新数据库字段:
Holiday.bulkWrite( holidays.map((holiday) => { const standardizedId = holiday.id.trim().toLowerCase(); return { updateOne: { filter: { holidayId: standardizedId }, update: { $set: { ...holiday, holidayId: standardizedId } }, upsert: true, }, }; }) )
2. 给holidayId添加唯一索引
仅靠代码逻辑的过滤无法强制字段唯一性,必须给holidayId建立唯一索引,让MongoDB从底层保证该字段不重复。
解决:
- 在Mongoose模型中配置:
const holidaySchema = new mongoose.Schema({ holidayId: { type: String, unique: true, required: true }, // 其他字段定义... }); - 或直接在MongoDB Shell中执行:
建立索引后,若出现重复的db.holidays.createIndex({ holidayId: 1 }, { unique: true })holidayId,MongoDB会抛出重复键错误,避免重复创建文档。
3. 排除_id字段(若存在)
如果从Google Calendar API返回的holiday对象中包含_id字段,upsert时会因为_id不匹配而创建新文档(MongoDB默认以_id作为主键)。
解决: 移除_id字段后再执行更新:
Holiday.bulkWrite( holidays.map((holiday) => { const { _id, ...cleanHoliday } = holiday; return { updateOne: { filter: { holidayId: holiday.id }, update: { $set: cleanHoliday }, upsert: true, }, }; }) )
内容的提问来源于stack exchange,提问作者BKM
相关产品推荐
相关产品推荐

