【问题标题】:Using Mongoose `pre` hook to get document before findOneAndUpdate()在 findOneAndUpdate() 之前使用 Mongoose `pre` 钩子获取文档
【发布时间】:2019-07-01 22:45:43
【问题描述】:

我正在尝试在我的 MongoDB 后端中使用 Mongoose prepost 钩子来比较文档在其保存前后的状态,以便根据更改触发一些其他事件。但是到目前为止,我无法通过 Mongoose pre 钩子获取文档。

根据文档“pre hooks 适用于 doc.save() 和 doc.update()。在这两种情况下,这都是指文档本身......”。所以我在这里尝试了。首先在我的模型/模式中,我有以下代码:

let Schema = mongoose
  .Schema(CustomerSchema, {
    timestamps: true
  })
  .pre("findOneAndUpdate", function(next) {
    trigger.preSave(next);
  })
  // other hooks
}

...然后在我的触发器文件中,我有以下代码:

exports.preSave = function(next) {
  console.log("this: ", this);
  }
};

但这是记录到控制台的内容:

this: { preSave: [Function], postSave: [AsyncFunction] }

很明显这不起作用。这并没有像我希望的那样注销文档。为什么this 不是这里的文档本身,正如文档本身所表明的那样?有没有办法用pre 钩子获取文档?如果没有,还有其他方法可以实现吗?

【问题讨论】:

    标签: mongodb mongoose


    【解决方案1】:

    您无法在 pre 挂钩中检索文档。

    根据documentation pre 是查询中间件,this 指的是查询而不是正在更新的文档。

    【讨论】:

    • Haris 是正确的,the docsfindOneAndUpdate 列为查询中间件(update 也是如此)。事实上,这些方法根本无法访问正在更新的文档。
    • 感谢 JohnnyHK 和 @Haris 的澄清。这些文档很难做出正面或反面。
    • 您可以在 /some/ pre 挂钩中检索文档。它取决于中间件的类型,而不是前/后。 findOneAndUpdate 是一个查询类型的中间件 - 但它不是专门的 pre
    • @scipilot 如何处理特定于文档的中间件,例如 remove()。我也没有得到这方面的背景。请在下面查看我的实现:Controller removes a object: await entity.remove(); Pre hook: entitySchema.pre('remove',async (next)=>{ console.log("removing",this); // ****** Output - removing {} await deleteAllUsersInEntity(this); await deleteAllThreadsInEntity(this); })
    • @HeetShah 最好打开一个新问题,提供更多背景信息,以便人们在那里为您提供帮助。但这应该可行:这应该是文件。你的文件是空的吗? {} 是一个空对象,根本不是什么。
    【解决方案2】:

    由于每种中间件函数中this 上下文的差异,会产生混淆。在 document prepost 中间件期间,您可以使用 this 访问文档模型,但不能在其他钩子中。

    中间件功能有三种,都有前阶段和后阶段。

    在文档中间件函数中,this 指的是文档(模型)。

    • init, validate, save, remove

    在查询中间件函数中,this 指的是查询

    • count,find,findOne,findOneAndRemove,findOneAndUpdate,update

    在聚合中间件中,this 指的是 聚合 对象。

    • aggregate

    这里有解释https://mongoosejs.com/docs/middleware.html#types-of-middleware

    因此,您可以在pre("init"), pre("init"), pre("validate"), post("validate"), pre("save"), post("save"), pre("remove"), post("remove") 期间简单地访问该文档,但不能在其他任何时候访问该文档。

    我已经看到人们在其他中间件挂钩中执行更多查询以再次找到模型的示例,但这对我来说听起来很危险。

    简短的回答似乎是,您需要将原始查询更改为面向文档,而不是查询或聚合样式。这似乎是一个奇怪的限制。

    【讨论】:

      【解决方案3】:

      根据文档,您的 pre hook 无法获取函数中的文档,但它可以获取如下查询

      schema.pre('findOneAndUpdate', async function() { 
       const docToUpdate = await this.model.findOne(this.getQuery());
       console.log(docToUpdate); // The document that findOneAndUpdate() will modify
      });
      

      【讨论】:

        【解决方案4】:

        如果你真的想在查询中间件函数

        中访问文档(或id)
        UserSchema.pre<User>(/^(updateOne|save|findOneAndUpdate)/, async function (next) {
          const user: any = this
        
          if (!user.password) {
            const userID = user._conditions?._id
            const foundUser = await user.model.findById(userID)
            ...
          }
        

        如果有人在用户密码更改时需要哈希密码的功能

        UserSchema.pre<User>(/^(updateOne|save|findOneAndUpdate)/, async function (next) {
          const user: any = this
        
          if (user.password) {
            if (user.isModified('password')) {
              user.password = await getHashedPassword(user.password)
            }
            return next()
          }
        
          const { password } = user.getUpdate()?.$set
          if (password) {
            user._update.password = await getHashedPassword(password)
          }
          next()
        })
        

        user.password 在“save”是触发器时存在

        user.getUpdate() 将返回“更新”触发器中更改的道具

        【讨论】:

          猜你喜欢
          • 2015-10-27
          • 1970-01-01
          • 2017-08-13
          • 2017-05-06
          • 1970-01-01
          • 2016-12-15
          • 2018-11-17
          • 2016-04-25
          • 2015-04-27
          相关资源
          最近更新 更多