【问题标题】:Mongoose $push keeps adding two entriesMongoose $push 不断添加两个条目
【发布时间】:2018-08-28 13:43:11
【问题描述】:

这是我的 userproduct 架构:

const productSchema = new Schema({
  //... 
  addedBy: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "users"
  }
});

const userSchema = new Schema({   
  //...
  addedItems: [{
    type: mongoose.Schema.ObjectId,
    ref: "products"
  }]
});

mongoose.model("products", productSchema);
mongoose.model("users", userSchema);

在我的 Node 后端路由中,我执行以下查询:

User.findOneAndUpdate(
  { _id: req.body.id },
  { $push: { addedItems: newProduct._id } },
  { upsert: true, new: true },
  function(err, doc) {
    console.log(err, doc);
  }
);

console.log 打印出这个:

{
    //...
    addedItems: [ 5ab0223118599214f4dd7803 ]
}

一切看起来都不错。我去实际使用我的 mongo db 的前端网站查看数据;我正在使用mlab.com,这就是显示的内容:

{
//...
"addedItems": [
        {
            "$oid": "5ab0223118599214f4dd7803"
        },
        {
            "$oid": "5ab0223118599214f4dd7803"
        }
    ]
}

问题:到底发生了什么?为什么它会在 addedItems 中添加一个额外的条目?!即使我的 console.log 只显示了一个。

注意:

我测试了后端路由是否被多次调用。它不是。

$push 似乎有问题,因为如果我只有{ addedItems: newProduct._id },那么只有一个条目进入,但它会覆盖整个数组。

编辑:

制作了一个测试项目以产生相同的结果:https://github.com/philliprognerud/test-mcve-stackoverflow

谁能弄清楚发生了什么?

【问题讨论】:

  • 也许你最终会调用它两次?
  • @Johnny 我很确定我不会两次调用“findOneAndUpdate”。我处理的代码也不多。所以我想知道这怎么可能发生。另外,由于我正在执行“new: true”,因此它应该返回最新的文档。而且它只显示一个。
  • 在更新之后我唯一要做的就是调用 res.send(...) ,它会回到我的 React 前端。请记住,这只会发生在“$push”中,如果我尝试只执行“{ addedItems: newProduct._id }”,那么只有一个条目会进入。但它显然会覆盖所有内容。

标签: node.js database mongodb mongoose mongoose-schema


【解决方案1】:

问题是由于您混合使用了 Promise(通过 async/await)和带有 findOneAndUpdate 调用的回调,最终执行了两次命令。

解决问题:

const updatedUser = await User.findOneAndUpdate(
  { id: userID },
  { $push: { addedItems: newProduct.id } },
  { upsert: true, new: true }
);

console.log(updatedUser);

未来的读者请注意,await 的使用未在问题中显示,但在 MCVE 中。

【讨论】:

  • 谢谢!解决方案有效,推理也有意义。
  • 您能解释一下为什么会这样吗?传递回调函数,为什么操作会执行两次?
  • @lucifer63 我猜是因为this
【解决方案2】:

我面临着类似的问题。刚登陆这个页面。我发现以前的答案不是很有描述性。所以发布这个:

export const updateUserHandler = async (req, res) => {
    const request = req.body;    
 await  User.findOneAndUpdate(                  //<== remove await 
  { _id: request.id },
  { $push: { addedItems: newProduct._id } },
  { upsert: true, new: true },
  (findErr, findRes) => {
        if (findErr) {
          res.status(500).send({
            message: 'Failed: to update user',
            IsSuccess: false,
            result: findErr
          });
        } else {
          res.status(200).send({
            message: 'Success:  to update user',
            IsSuccess: true,
            result: findRes
          });

        }
      }
);
  }

这里有两个异步调用,一个是 async,另一个是 await。因此,文档中有两个条目。只需从 await User.findOneAndUpdate 中删除 await。它将完美地工作。 谢谢!!

【讨论】:

    【解决方案3】:

    当您等待 Query 时,您正在使用类似于 Promise 的,特别是 Query 的 .then().catch(()。传递回调也会导致您描述的行为。

    如果同时 await Query 和 .then() 的 Query,会使查询执行两次

    使用:

    await Model.findOneAndUpdate(query, doc, options)
    

    Model.findOneAndUpdate(query, doc, options, callback)
    

    【讨论】:

      【解决方案4】:

      此代码 $push 不断添加两个条目: const ali={ "_id": "5eaa39a18e7719140e3f4430" };

      //   return await customerModel.findOneAndUpdate(
      //     ali,
      //     {
      //       "$push": {
      //         "address": [objAdr],
      //       },
      //     },
      //     function (error: any, success: any) {
      //       if (error) {
      //         console.log(error);
      //       } else {
      //         console.log(success);
      //       }
      //     }
      //   );
      

      我的解决方案确实有效:

      return await customerModel
      .findOneAndUpdate(
        { _id: ids },
        { $push: { "address": objAdr } }
      )
      .catch((err: string | undefined) => new Error(err));
      

      【讨论】:

        猜你喜欢
        • 2015-06-01
        • 1970-01-01
        • 2012-05-21
        • 1970-01-01
        • 1970-01-01
        • 2017-01-19
        • 1970-01-01
        • 1970-01-01
        • 2012-12-10
        相关资源
        最近更新 更多