You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose无法自定义_id:更新数据时_id未插入且upsert无效

嘿,我来帮你搞定这个upsert和_id不生效的问题!先拆解下你代码里的几个关键坑,再给你修正后的方案:

问题根源分析

  1. 路由参数获取失败:你的路由是router.put('/', ...),但代码里用了req.params.exchange、req.params.symbol——这些参数根本不会存在!因为你的路由没定义参数占位符,导致filter拼接出来的值是undefinedundefined,自然匹配不到任何文档,upsert也会因为参数异常出问题。
  2. _id设置方式错误:MongoDB不允许修改已存在文档的_id,而且直接把_id放在更新对象里,upsert时也不会生效——你需要用$setOnInsert操作符,只在插入新文档时设置_id。
  3. 使用了废弃的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:14:45