如何在Mongoose中结合lastUpdated条件执行updateOne批量更新?
问题
使用Mongoose时需要实现以下文档更新逻辑:
- 获取新批次
matches数据后,按id匹配数据库中的文档 - 仅当新文档的
lastUpdated字段晚于数据库文档时才执行更新 - 支持upsert新增不存在的文档
- 不能将
id和lastUpdated同时作为filter条件,否则会产生重复数据
尝试用bulkWrite结合arrayFilters实现时出现MongoBulkWriteError错误,以下是相关信息:
测试数据
数据库现有数据
const testMatchesOnDB = [ { id: 1, team: "teamOneV1", lastUpdated: moment().subtract(10, "days") }, { id: 2, team: "TeamTwoV1", lastUpdated: moment("2022-08-29T14:18:59+01:00"), }, { id: 3, team: "TeamThreeV1", lastUpdated: moment().subtract(2, "days") }, ];
新获取的API数据
const newMatchesFromAPI = [ { id: 2, team: "TeamTwoV2", lastUpdated: moment("2022-08-29T14:18:59+01:00"), }, { id: 3, team: "TeamThreeV2", lastUpdated: moment().subtract(1, "days") }, { id: 4, team: "TeamFourV2", lastUpdated: moment() }, // 测试upsert ];
尝试的代码
await Test.bulkWrite( newMatchesFromAPI.map((match) => ({ updateOne: { filter: { id: match.id, }, update: match, upsert: true, arrayFilters: [{ lastUpdated: { $lt: moment(match.lastUpdated) } }], }, })) );
错误信息
MongoBulkWriteError: The array filter for identifier 'lastUpdated' was not used in the update { $setOnInsert: { id: 2 } }
期望的数据库结果
const desiredDB = [ { id: 1, team: "teamOneV1", lastUpdated: moment().subtract(10, "days") }, // 未匹配,保持不变 { id: 2, team: "TeamTwoV1", lastUpdated: moment("2022-08-29T14:18:59+01:00"), // 日期相等,不更新 }, { id: 3, team: "TeamThreeV2", lastUpdated: moment().subtract(1, "days") }, // 更新为较新数据 { id: 4, team: "TeamFourV2", lastUpdated: moment() }, // upsert新增 ];
正确实现方式
之前的错误原因是arrayFilters仅用于数组元素的条件更新,不适用于顶层字段的判断。要实现需求,需要结合$set和更新条件,同时利用upsert特性:
await Test.bulkWrite( newMatchesFromAPI.map((match) => ({ updateOne: { filter: { id: match.id }, update: { $set: { ...match, lastUpdated: match.lastUpdated.toDate() // 转换为MongoDB支持的Date类型 } }, upsert: true, // 设置更新条件:仅当数据库文档的lastUpdated更旧,或文档不存在时执行操作 condition: { $or: [ { lastUpdated: { $lt: match.lastUpdated.toDate() } }, { lastUpdated: { $exists: false } } ] } } })) );
逻辑说明
- 仅用id作为filter:确保按唯一标识匹配文档,避免重复数据
- $set批量更新字段:将新数据的所有字段同步到匹配文档中
- condition控制更新时机:
- 当数据库存在文档时,仅在
lastUpdated小于新数据时才执行更新 - 当文档不存在时(upsert场景),
lastUpdated不存在的条件成立,直接插入新文档
- 当数据库存在文档时,仅在
- 日期类型转换:用
toDate()将moment对象转为MongoDB原生Date类型,避免类型不匹配问题
结果验证
- id=1:未匹配新数据,保持原有状态
- id=2:新数据与数据库的lastUpdated日期相等,不满足更新条件,保留原数据
- id=3:新数据的lastUpdated更晚,执行字段更新
- id=4:数据库中无匹配文档,自动upsert新增
内容的提问来源于stack exchange,提问作者miguelmigs24
相关产品推荐
相关产品推荐

