在Express路由中使用MongoDB的$inc操作时遇到问题
问题排查:MongoDB quantity字段扣除数值未生效的原因
你遇到的这个问题很典型——程序没报错但数据库毫无变化,结合你的代码来看,主要有几个关键问题:
1. 致命的拼写错误
看你代码里这一行:
item.udpdate({ $inc: { quantity: parseInt(soldQuantity) * -1 } });
这里把update拼写成了udpdate!JavaScript在非严格模式下访问对象不存在的方法不会抛出错误,所以程序没报错,但这条更新语句根本就没被执行,自然quantity字段不会有变化。这应该是最直接的原因。
2. Mongoose实例更新方式错误
就算你把拼写改对了,用item.update()也不是Mongoose文档实例的正确更新方法。Mongoose的文档实例(也就是你通过findById拿到的item)应该用updateOne(),或者直接修改实例属性后调用save()——更推荐后者,因为你已经在操作这个实例了。
3. 异步操作未正确处理+冗余操作
你在item.save().then()里调用更新,但update本身也是异步操作,你没有等待它完成;而且先save一次(保存solditems),再单独update quantity,其实完全可以合并成一次数据库操作,既高效又避免潜在的异步问题。
修正后的代码示例
我把你的代码改成了async/await风格(比回调更清晰),同时修复了所有问题:
router.post("/item/:id", middleware.isLoggedIn, async (req, res) => { try { // 1. 找到目标商品 const item = await Item.findById(req.params.id); if (!item) { req.flash("error", "Item not found"); return res.redirect("/products"); } // 2. 构造售出记录数据 const soldQuantity = parseInt(req.body.soldQuantity); const soldItem = { id: item._id, item: item.name, askPrice: item.price }; const itemSold = { soldItem, soldPrice: req.body.soldPrice, soldQuantity }; // 3. 创建售出记录 const sold = await SoldItem.create(itemSold); // 4. 合并更新商品数据:添加售出记录 + 扣除库存 item.solditems = item.solditems || []; item.solditems.push(sold); item.quantity -= soldQuantity; // 直接修改库存属性 await item.save(); // 一次save完成两个修改 req.flash("success", "Item Sold"); res.redirect("/products"); } catch (err) { console.error(err); req.flash("error", "Failed to process sale"); res.redirect("back"); } });
更高效的写法:用Model的updateOne一次完成
如果你不想先查询再修改,也可以直接用Item.updateOne来一次完成两个操作,减少数据库查询次数:
// 替换上面的步骤4 await Item.updateOne( { _id: req.params.id }, { $push: { solditems: sold }, $inc: { quantity: -soldQuantity } } );
至于为什么Mongo Shell里能正常执行,很简单——你在Shell里写的是正确的语法,没有拼写错误,而且用的是MongoDB原生的更新逻辑,自然能生效。
内容的提问来源于stack exchange,提问作者Darrell
相关产品推荐
相关产品推荐

