Mongoose $ push不断添加两个条目

时间:2018-03-19 21:12:08

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

以下是我的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

有人能弄清楚发生了什么吗?

4 个答案:

答案 0 :(得分:6)

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

解决问题:

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

console.log(updatedUser);

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

答案 1 :(得分:1)

此代码$ 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));

答案 2 :(得分:0)

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

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 :(得分:0)

在等待查询时,您使用的是类似诺言的查询,特别是Query的.then().catch(()。同时传递回调也会导致您描述的行为。

如果同时等待查询和查询的.then(),则会使查询执行两次

使用:

await Model.findOneAndUpdate(query, doc, options)

OR

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