Mongoose-MongoDB:循环查询出现"Query was already executed"错误
问题解决:"Query was already executed" 错误与唯一ID生成优化
错误原因
你的代码存在两个核心问题:
- 异步时序混乱:Mongoose的
findOne传入回调函数时,不会返回Promise,导致await完全失效。while循环会在查询未完成时就重复执行,疯狂触发同一个查询操作,最终抛出"Query was already executed"错误。 - 回调与async/await混用:回调函数里的
async不仅多余,还会导致unique变量的修改无法同步到外层循环的判断逻辑中,循环无法正确终止。
修复后的代码
我们改用纯async/await风格重构createRandomID,同时让函数支持复用:
// 通用唯一ID生成函数,支持不同模型和字段 async function createRandomID(model, fieldName = 'deedId') { let id; let existingRecord; do { // 生成4位大写随机字符串 id = Math.random().toString(36).slice(2, 6).toUpperCase(); // 等待查询完成,无回调 existingRecord = await model.findOne({ [fieldName]: id }); } while (existingRecord); // 存在重复则重新生成 return id; } const addNewDeed = asyncErrorWrapper(async (req, res, next) => { let information = req.body; // 传入Deed模型,使用默认字段deedId const deedId = await createRandomID(Deed); /*Other Operations...*/ });
关键优化点
- 移除回调,用await等待查询:确保每次循环都等待数据库查询完成后再执行下一步,避免重复触发同一查询。
- 通用化设计:通过传入
model和fieldName参数,让函数可以在其他业务场景复用,比如生成用户表的userId等。 - do-while逻辑更合理:先生成ID再检查,符合"生成-校验"的业务流程。
增强鲁棒性建议
- 增加ID长度:4位随机字符串的碰撞概率约为1/(36^4)=1/1679616,业务量较大时建议增加到6位,减少循环次数。
- 数据库层面唯一约束:在Deed模型的
deedId字段上添加唯一索引:
然后在生成函数中捕获唯一索引冲突错误(错误码const deedSchema = new Schema({ deedId: { type: String, unique: true, required: true }, // 其他字段... });11000),处理并发场景下的ID碰撞:async function createRandomID(model, fieldName = 'deedId') { let id; while (true) { id = Math.random().toString(36).slice(2, 8).toUpperCase(); // 6位ID try { const existingRecord = await model.findOne({ [fieldName]: id }); if (!existingRecord) return id; } catch (err) { // 处理并发导致的唯一索引冲突 if (err.code === 11000) continue; throw err; } } }
内容的提问来源于stack exchange,提问作者firstlord
相关产品推荐
相关产品推荐

