Mongoose无法自定义_id:更新数据时_id未插入且upsert无效
嘿,我来帮你搞定这个upsert和_id不生效的问题!先拆解下你代码里的几个关键坑,再给你修正后的方案:
问题根源分析
- 路由参数获取失败:你的路由是
router.put('/', ...),但代码里用了req.params.exchange、req.params.symbol——这些参数根本不会存在!因为你的路由没定义参数占位符,导致filter拼接出来的值是undefinedundefined,自然匹配不到任何文档,upsert也会因为参数异常出问题。 - _id设置方式错误:MongoDB不允许修改已存在文档的
_id,而且直接把_id放在更新对象里,upsert时也不会生效——你需要用$setOnInsert操作符,只在插入新文档时设置_id。 - 使用了废弃的update方法:Mongoose的
update已经被标记为废弃,推荐用updateOne/updateMany来明确操作意图,避免潜在的行为不一致。
修正后的完整代码
首先,先把路由改成带参数的形式,再调整更新逻辑:
/* UPDATE LiveFeed socket */ // 先修正路由,添加参数占位符才能拿到exchange和symbol router.put('/:exchange/:symbol', async function(req, res, next) { try { // 解构参数,让代码更清晰 const { exchange, symbol, timestamp, ltp, close, open, high, low } = req.params; const filterKey = exchange + symbol; // 使用updateOne替代废弃的update方法 const updateResult = await LiveFeed.updateOne( // 查询条件:匹配filter字段等于filterKey的文档 { filter: filterKey }, { // $set:不管是更新还是插入,都设置这些字段 $set: { exchange, symbol, timestamp, ltp, close, open, high, low }, // $setOnInsert:只有在upsert触发插入新文档时,才设置_id $setOnInsert: { _id: filterKey } }, // 开启upsert功能 { upsert: true } ); // 返回结果给前端 res.status(200).json({ message: updateResult.upsertedCount > 0 ? '新文档已插入' : '现有文档已更新', data: updateResult }); } catch (error) { // 把错误传递给错误处理中间件 next(error); } });
额外注意事项
别忘了检查你的LiveFeed Schema!如果要使用自定义字符串作为_id,必须在Schema里显式声明类型:
const mongoose = require('mongoose'); const liveFeedSchema = new mongoose.Schema({ _id: String, // 显式指定_id为字符串类型,否则默认是ObjectId filter: String, exchange: String, symbol: String, timestamp: Number, // 根据你的实际数据类型调整,比如String/Date ltp: Number, close: Number, open: Number, high: Number, low: Number }); const LiveFeed = mongoose.model('LiveFeed', liveFeedSchema); module.exports = LiveFeed;
核心逻辑说明
$setOnInsert:这个操作符只会在upsert触发插入新文档时执行,完美避开了MongoDB不允许修改已有文档_id的限制。updateOne:明确只更新匹配到的第一个文档,语义更清晰,也符合你当前的业务场景。- 异步处理:用
async/await替代回调,代码可读性更高,也更容易捕获错误。
内容的提问来源于stack exchange,提问作者RRD
相关产品推荐
相关产品推荐

