如何在findOneAndUpdate中检测用户名不存在时插入并执行后续逻辑
如何在findOneAndUpdate中处理不存在的用户名并执行相同失败登录流程?
最有效的实现方式是利用MongoDB findOneAndUpdate 的upsert: true选项,这样可以用一次原子操作完成"更新已有用户"或"插入新用户",不需要额外的查询和插入步骤,既高效又能避免并发问题。
你的尝试里手动插入再递归调用的方式会增加数据库操作次数,还可能带来并发风险,而开启upsert是更简洁可靠的方案。下面是修改后的完整代码:
function _lockIfFailedAttemptsReachLimit(username, req, res, next) { colUser.findOneAndUpdate( { username }, { // 设置失败登录时间 $set: { failedLoginAt: new Date() }, // 递增失败次数(插入时会自动初始化为1) $inc: { nFailedLogin: 1 }, // 可选:插入新用户时设置默认字段,比如账户锁定状态默认未锁定 $setOnInsert: { isLocked: false } }, { upsert: true, // 开启upsert:找不到匹配文档则插入新文档 returnOriginal: false }, (err, result) => { if (err) { next('Undefined error!'); return; } // 不管是更新还是插入,result.value都会返回对应的文档 let foundUser = result.value; let nFailed = _.get(foundUser, 'nFailedLogin', 0); let errMsg = `Invalid username or password. You have tried ${nFailed}/${MAX_FAILED_LOGIN_ATTEMPTS} login attempts`; if (nFailed < MAX_FAILED_LOGIN_ATTEMPTS) { next(errMsg); } else { _lockAccount(username, (err, lockResult) => { if (!err) { errMsg += `. Your account has been locked for ${WAITING_TIME_AFTER_LOCKED} seconds.`; } next(errMsg); }); } } ); }
关键修改说明:
- 开启
upsert: true:这是核心改动,MongoDB会自动判断用户名是否存在——存在则执行更新逻辑,不存在则插入新文档,全程是原子操作,不会出现中间状态。 $inc的自动初始化特性:当插入新文档时,$inc: { nFailedLogin: 1 }会自动把nFailedLogin设为1,正好对应第一次失败尝试的场景,完全符合你的需求。- 可选的
$setOnInsert:如果新插入的用户需要一些默认字段(比如账户锁定状态isLocked默认false),可以用$setOnInsert指定,这些字段只会在插入新文档时生效,更新已有文档时不会被修改。 - 修正代码失误:你原来在找不到用户时获取客户端IP作为username插入,这应该是个错误,我们直接使用传入的
username参数,确保插入的是尝试登录的目标用户名。
流程逻辑:
- 无论用户名是否存在,一次
findOneAndUpdate操作就能完成失败登录记录的更新/初始化。 - 后续的失败次数判断、账户锁定逻辑和原来完全一致,不需要额外调整。
内容的提问来源于stack exchange,提问作者user1314404
相关产品推荐
相关产品推荐

