【问题标题】:Mongo `pre` hook not firing as expected on `save()` operationMongo `pre` 钩子在`save()` 操作中未按预期触发
【发布时间】:2019-02-21 01:16:11
【问题描述】:

我在我的 MongoDB/Node 后端使用 prepost 钩子来比较文档的预保存和保存后版本,以便我可以根据更改的内容通过模型触发器生成注释。在我的一个模型/集合中,这是有效的,但在另一个模型/集合中,它没有按预期工作,我不知道为什么。

在问题案例中,一些研究已经确定,即使我在使用 save() 的操作上调用 pre 钩子触发器,但当我控制台输出在该 pre 钩子中传递的文档状态时,它是已经应用了更改。换句话说,据我所知,钩子不是在save() 操作之前触发,而是在之后触发。

这是我的相关型号代码:

let Schema = mongoose
  .Schema(CustomerSchema, {
    timestamps: true
  })
  .pre("save", function(next) {
    const doc = this;
    console.log("doc in .pre: ", doc); // this should be the pre-save version of the doc, but it is the post-save version
    console.log("doc.history.length in model doc: ", doc.history.length);
    trigger.preSave(doc);
    next();
  })
  .post("save", function(doc) {
    trigger.postSave(doc);
  })
  .post("update", function(doc) {
    trigger.postSave(doc);
  });

module.exports = mongoose.model("Customer", Schema);

我正在执行的save() 操作的相关部分如下所示(我所做的只是将一个新元素推送到文档中名为“history”的数组):

exports.updateHistory = async function(req, res) {
  let request = new CentralReqController(
    req,
    res,
    {
      // Allowed Parameters
      id: {
        type: String
      },
      stageId: {
        type: String
      },
      startedBy: {
        type: String
      }
    },
    [
      // Required Parameters
      "id",
      "stageId",
      "startedBy"
    ]
  );

  let newHistoryObj = {
    stageId: request.parameters.stageId,
    startDate: new Date(),
    startedBy: request.parameters.startedBy,
    completed: false
  };

  let customerToUpdate = await Customer.findOne({
    _id: request.parameters.id
  }).exec();

  let historyArray = await customerToUpdate.history;

  console.log("historyArray.length before push in update func: ", historyArray.length);

  historyArray.push(newHistoryObj);

  await customerToUpdate.save((err, doc) => {
    if (doc) console.log("history update saved...");
    if (err) return request.sendError("Customer history update failed.", err);
  });
};

所以,我的问题是,如果 save() 操作上的 pre 钩子应该在 save() 发生之前触发,为什么我通过 console.log 查看的文档会显示已经拥有的文档对save()做了操作吗?

【问题讨论】:

    标签: mongodb mongoose


    【解决方案1】:

    您对“保存”前/后挂钩的作用有点误解。在前/后挂钩术语中,save 是对数据库的实际保存操作。话虽如此,thispre('save') 挂钩中的this 是您在其上调用.save() 的对象,而不是数据库中更新的对象。例如:

    let myCustomer = req.body.customer; // some customer object
    
    // Update the customer object
    myCustomer.name = 'Updated Name';
    
    // Save the customer
    myCustomer.save();
    

    我们刚刚更新了客户名称。当.save() 被调用时,它会触发钩子,就像你上面所说的那样。唯一不同的是,pre('save') 钩子中的this与 myCustomer 相同的对象,而不是数据库中的更新对象。相反,`post('save') 挂钩中的doc 对象来自数据库的更新对象。

    Schema.pre('save', function(next) {
      console.log(this); // Modified object (myCustomer), not from DB
    )};
    
    Schema.post('save', function(doc) {
      console.log(doc); // Modified object DIRECTLY from DB
    });
    

    【讨论】:

    • 所以,澄清一下,您是说pre 保存挂钩实际上根本没有从数据库中读取文档?您确定吗?在我看来,这似乎是因为同样的设置适用于我的其他收藏 - 如果你说的是真的,我不明白它怎么可能。
    • 就是这样!!试试看,在next() 函数上设置一个超时,然后继续检查你的数据库。您的pre('save)' 将被调用,并且在调用next() 之前不会更改数据库中的对象
    • 我在上面添加了我的完整功能代码。请检查并确认这对您来说仍然是问题。
    • 正确,next()决定什么时候去下一个函数,所以当你调用next()时,下一步就是上传到数据库了。
    • 如果您不相信我所说的话,请尝试更改pre('save') 中的字段。更新客户名称或其他内容。您会看到它在数据库中发生了更改,因为实际的数据库更新发生在 pre('save') 之后
    猜你喜欢
    • 2021-05-17
    • 2018-11-17
    • 2015-07-20
    • 1970-01-01
    • 1970-01-01
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 2013-07-19
    相关资源
    最近更新 更多