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

MongoDB基于$ne和arrayFilters的$push更新失效问题排查

问题描述

在MERN应用中开发商品过期日期管理的Patch API,需求是:给定商品UPC和过期日期,仅当该日期未存在于对应商品的expiryDates数组时,将其添加进去。

示例数据

[
  {
    "_id": {"$oid":"6795e982c4e5586be7dc5bfc"},
    "section":"Dairy",
    "products": [
      {
        "productUPC":"068700115004",
        "name":"Dairyland 2% Milk Carton 2L",
        "expiryDates": [
          {
            "dateGiven":"2025-01-30T00:00:00.000-06:00",
            "discounted":true
          }
        ]
      },
      {
        "productUPC":"068700011825",
        "name":"Dairyland 1% Milk Carton 2L",
        "expiryDates":[]
      }
    ]
  }
]

现有Patch API代码

router.patch("/products/:productUPC&:expiryDate", async (req, res) => {
    try {
        let collection = await db.collection("storeSections");
        let result = await collection.updateOne({
          "products.expiryDates.dateGiven": {
            $ne: new Date(moment(req.params.expiryDate)).toISOString(true)
          }
          },
          {
            $push: {
              "products.$[x].expiryDates": {
                "dateGiven": new Date(moment(req.params.expiryDate)).toISOString(true),
                "discounted": false
              }
            }
          },
          {
            arrayFilters: [
              {
                "x.productUPC": req.params.productUPC
              }
            ]
          })
          
        res.send(result).status(200);
    } catch(err) {
        console.error(err);
        res.status(500).send("Error updating record.");
    }
});

测试请求示例

localhost:5050/record/products/productUPC=068700115004&expiryDate=2025-02-21

该操作在MongoDB Playground中可正常执行,但API调用后无效果,需要排查原因并修复。


原因分析与修复方案

1. 路由参数与请求格式不匹配

现有路由定义为/products/:productUPC&:expiryDate,这是把productUPC&expiryDate当作混合路由参数,但实际请求用的是查询参数(productUPC=xxx&expiryDate=xxx),导致req.params无法正确获取两个参数的值。

修复:将路由改为接受查询参数,简化路由路径为/products,通过req.query获取参数:

router.patch("/products", async (req, res) => {
  const { productUPC, expiryDate } = req.query;
  // ... 后续代码
});

对应请求URL调整为:

localhost:5050/record/products?productUPC=068700115004&expiryDate=2025-02-21

2. 更新条件逻辑错误

原代码的查询条件"products.expiryDates.dateGiven": { $ne: ... }是检查整个文档中是否存在任意一个产品的过期日期不等于给定值,逻辑偏差明显:只要文档内有其他产品的过期日期和给定值不同,就会匹配文档,但无法确保目标UPC的产品没有该过期日期。

正确做法:先定位包含目标UPC的文档,再通过数组过滤器配合条件,确保目标产品的expiryDates中不存在该日期。

修复:调整updateOne的查询条件和数组过滤器:

let result = await collection.updateOne(
  {
    "products.productUPC": productUPC // 先匹配包含目标UPC的文档
  },
  {
    $push: {
      "products.$[x].expiryDates": {
        dateGiven: targetDate,
        discounted: false
      }
    }
  },
  {
    arrayFilters: [
      {
        "x.productUPC": productUPC,
        // 确保当前产品的expiryDates中没有该日期
        "x.expiryDates.dateGiven": { $ne: targetDate }
      }
    ]
  }
);

3. 日期处理与响应顺序问题

  • 日期处理:直接用moment(expiryDate).utc().toISOString()统一格式为UTC时间的ISO字符串,避免先转Date再转ISOString导致的时区不一致问题;
  • 响应顺序:res.send(result).status(200)会先发送响应再设置状态码,导致状态码不生效,需改为res.status(200).send(result)。

完整修复后的代码

router.patch("/products", async (req, res) => {
  try {
    const { productUPC, expiryDate } = req.query;
    if (!productUPC || !expiryDate) {
      return res.status(400).send("Missing productUPC or expiryDate parameter");
    }
    
    // 统一日期格式为UTC时区的ISO字符串
    const targetDate = moment(expiryDate).utc().toISOString();
    
    let collection = await db.collection("storeSections");
    let result = await collection.updateOne(
      {
        "products.productUPC": productUPC
      },
      {
        $push: {
          "products.$[x].expiryDates": {
            dateGiven: targetDate,
            discounted: false
          }
        }
      },
      {
        arrayFilters: [
          {
            "x.productUPC": productUPC,
            "x.expiryDates.dateGiven": { $ne: targetDate }
          }
        ]
      }
    );

    res.status(200).send(result);
  } catch(err) {
    console.error(err);
    res.status(500).send("Error updating record.");
  }
});

内容的提问来源于stack exchange,提问作者Ian McInnes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:32:10