【问题标题】:findOneAndUpdate causing duplication problemfindOneAndUpdate 导致重复问题
【发布时间】:2019-08-01 20:31:38
【问题描述】:

我在猫鼬的 findOneAndUpdate 中遇到问题。 情况是我通过查找来更新文档。 查询如下:

UserModel.findOneAndUpdate({
individualId: 'some id'
}, {
$push: {
supporterOf: 'some string'
}
})

'supporterOf' 是 UserModel 的 ref,它的类型是 'ObjectId'。

我在这里面临的问题是,“某些字符串”在文档中的“supporterOf”下被推送了两次。

谁能告诉我如何在文档中推送一个数组元素?

【问题讨论】:

  • 您的查询看起来不错,我相信您的问题出在代码的其他地方。你能发布你的端点的其余部分吗?您是否尝试过手动获取文档、将字符串推送到数组并使用 .save() 保存?
  • 不,我没有尝试过手动获取然后更新 .save() 因为我想一次性完成交易。我想查找并更新结果,它更新但插入重复项。我也调试了代码,我从端点接收单个值。你能告诉我,当我(手动获取和推送)和我(使用 findOneAndUpdate)时,交易时间会相同吗?
  • 它会慢一些,但除非您计划同时更新许多(例如 1000 多个)文档,或者硬件限制非常低,否则您应该可以忽略这些差异。不过,我建议您尝试进一步调试此问题,因为我相信可能存在潜在问题,可能会导致进一步的问题。因此,您能否发布您的问题的概念证明?
  • 是的@BenSower,你是对的,它会更慢,但相信我现在正在发生 findOneAndUpdate 在推送项目中插入重复条目。为了再次出现此问题,请创建一个包含空数组的模式。然后运行 ​​findOneAndUpdate 并像上面的帖子一样推送项目,你会遇到重复条目的问题。
  • 我创建了这个要点gist.github.com/BenSower/9800a21c2ae4202d81a46fc64bc55b9e,它调用 findOneAndUpdate 两次,每次只推送一个字符串。你用的是什么猫鼬版本?

标签: arrays node.js mongodb mongoose array-push


【解决方案1】:

我遇到了同样的问题,解决办法是。

我一直在等待,如下所示。

 **await** schema.findOneAndUpdate(queryParms, {
                "$push": {
                    "array1": arrayDetails,
                    "array2": array2Details
                }
            }, {
                "upsert": true,
                "new": true
            },
            function (error, updateResponse) {
                if (error) {
                    throw new Error (error);
                } else {
                    // do something with updateResponse;
                }
            });

简单地删除 await 帮助我解决了这个问题。 需要找到根本原因。 欢迎任何参考指针。

【讨论】:

【解决方案2】:

我最近遇到了同样的问题。但是,我设法通过其他一些逻辑(下面给出详细信息)克服了这个问题,但无法理解为什么 findOneAndUpdate 在 mongodb 中插入 duplicate 条目的原因。

您可以通过遵循逻辑来克服这个问题。

使用 findOnefindById 而不是 findOneAndUpdate 在您的集合中搜索文档,然后手动更新您的文档并运行 save ().

你可以用这段代码sn-p更好地理解

return new Promise(function (resolve, reject) {
    Model.findOne({
            someCondition...
        }, function (err, item) {
            if (err) {
                reject(err);
            } else {
                item.someArray.push({
                    someKeyValue...
                });
                item.save().then((result) => {
                    resolve(result)
                }).catch((err) => {
                    reject(err)
                });
            }
        }).catch((err) => {
            reject(err)
        });
   });

这不会插入重复的项目。但是,如果你知道重复背后的原因,一定要更新这个帖子。

【讨论】:

  • @Faizy 你知道 mongoose 也直接返回一个 Promise(除非你使用相当旧的版本),所以你不必再次将它包装在另一个 Promise 中?
  • @BenSower 基本上 Asad Ullah 已经告诉我重复条目的解决方法。我想否定数据库中的重复条目,现在 findOneAndUpdate 导致重复条目的问题。
  • 是的,但这并没有改变可以使用更易于阅读的函数改进代码风格的事实 ;-)
【解决方案3】:

问题似乎源于结合了等待和回调。在我意识到我正在使用 (err, resp) 回调 .catch(...) 之前,我遇到了同样的问题。

models[auxType].findOneAndUpdate(
    filter,
    updateObject,
    options,
    (err, resp)=>{
        if (err) {
            console.log("Update failed:",err)
            res.json(err)
        } else if (resp) {
            console.log("Update succeeded:",resp)
            res.json(resp)
        } else {
            console.log("No error or response returned by server")
        }
    })
    .catch((e)=>{console.log("Error saving Aux Edit:",e)}); // << THE PROBLEM WAS HERE!!

删除 .catch(...) 行后问题就解决了。

来自猫鼬文档:

【讨论】:

    【解决方案4】:

    接受答案的问题在于,当 findOneAndUpdate() 方法已经返回了一个 Promise 时,它​​只能通过将其包装在一个不必要的附加 Promise 中来解决问题。此外,它同时使用了 Promise 和回调,这是你几乎不应该做的事情。

    相反,我会采取以下方法:

    我通常喜欢将我的更新查询逻辑与其他关注点分开,以提高可读性和可重用性。所以我会做一个类似的包装函数:

    const update = (id, updateObj) => {
        const options = {
          new: true,
          upsert: true
        }
        return model.findOneAndUpdate({_id: id}, {...updateObj}, options).exec()
    }
    

    然后可以在整个应用程序中重复使用此函数,使我不必重写重复的选项设置或执行调用。

    然后我将拥有一些其他函数来负责调用我的查询、将值传递给它并处理从它返回的内容。

    有点像:

    const makePush = async () => {
       try {
         const result = await update('someObjectId', {$push: {someField: value}});
         // do whatever you want to do with the updated document
       catch (e) {
         handleError(e)
        }
     }
    

    无需创建不必要的承诺,没有回调地狱,没有重复请求,并且更加遵守单一职责原则。

    【讨论】:

      【解决方案5】:

      使用 $addToSet 而不是 $push,应该可以解决问题。我认为创建猫鼬“模型”时使用的数据结构存在问题。我们知道 push 是一个数组(允许重复)操作,而 addToSet 可能是一个 Set 操作(集合不允许重复)。

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题。我的代码是:

        const doc = await model.findOneAndUpdate(
        {filter}, {update},
        {new: true}, (err, item) =>  if(err) console.log(err) }
        )
        res.locals.doc = doc
        next();
        

        问题是,出于某种原因,“新”选项之后的回调创建了一个双重条目。我删除了回调并且它起作用了。

        【讨论】:

          【解决方案7】:

          我遇到了同样的问题。 我为我找到了解决方案:

          我同时使用了回调和承诺(所以使用关键字“await”)。

          同时使用回调和承诺将导致查询被执行两次。您应该使用其中一种,但不能同时使用。

            options = {
              upsert: true  // creates the object if it doesn't exist. defaults to false.
            };
            await Company.findByIdAndUpdate(company._id,
              { $push: { employees: savedEmployees } },
              options,
              (err) => {
                 if (err) {
                    debug(err);
                 }
              }
            ).exec();
          

            options = {
              upsert: true  // creates the object if it doesn't exist. defaults to false.
            };
            await Company.findByIdAndUpdate(company._id,
              { $push: { employees: savedEmployees } },
              options
            ).exec();
          

          【讨论】:

            【解决方案8】:
            UserModel.findOneAndUpdate(
            { _id: id },
            { object }
            )
            

            即使您使用 _id 作为参数,也不要忘记通过 id 明确过滤器

            【讨论】:

              【解决方案9】:

              就我而言,更改 async 回调解决了问题。

              改变这个:

              await schema.findOneAndUpdate(
                  { queryData },
                  { updateData },
                  { upsert: true },
                  (err) => {
                    if (err) console.log(err); 
                    else await asyncFunction();
                  }
                );
              

              到这里:

              await schema.findOneAndUpdate(
                  { queryData },
                  { updateData },
                  { upsert: true },
                  (err) => {
                    if (err) console.log(err);
                  }
                );
               if (success) await asyncFunction();
              

              【讨论】:

                【解决方案10】:

                $addToSet 而不是 $push 让我可以防止像这样在 User 文档的 mongoDb 数组字段中重复输入。

                const blockUserServiceFunc = async(req, res) => {
                
                let filter = {
                    _id : req.body.userId
                }
                
                let update = { $addToSet: { blockedUserIds:  req.body.blockUserId  } };
                
                await User.findOneAndUpdate(filter, update, (err, user) => {
                    if (err) {
                        res.json({
                            status: 501,
                            success: false,
                            message: messages.FAILURE.SWW
                        });
                    } else {
                
                        res.json({
                            status: 200,
                            success: true,
                            message: messages.SUCCESS.USER.BLOCKED,
                            data: {
                                'id': user._id,
                                'firstName': user.firstName,
                                'lastName': user.lastName,
                                'email': user.email,
                                'isActive': user.isActive,
                                'isDeleted': user.isDeleted,
                                'deletedAt': user.deletedAt,
                                'mobileNo': user.mobileNo,
                                'userName': user.userName,
                                'dob': user.dob,
                                'role': user.role,
                                'reasonForDeleting': user.reasonForDeleting,
                                'blockedUserIds': user.blockedUserIds,
                                'accountType': user.accountType
                            }
                        });
                
                    }
                }
                ).catch(err => {
                    res.json({
                        status: 500,
                        success: false,
                        message: err
                    });
                });
                

                }

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-11-29
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-02-05
                  • 2020-03-13
                  相关资源
                  最近更新 更多