Mongoose 中的钩子机制深入解析

在使用 MongoDB 作为数据库时,Mongoose 是 Node.js 中最广泛使用的 MongoDB 的 ODM 库,它提供了许多方便的 API 用于操作 MongoDB 数据库,如定义模型,查询,更新等。同时,Mongoose 还提供了一种叫做钩子(hook)的机制,用于在模型中的特定事件发生时执行一些操作,这个钩子机制十分强大,能够帮助我们更加灵活和方便地处理数据。本篇文章将会对 Mongoose 的钩子机制进行深入解析,并通过示例代码带领大家学习如何使用和优雅地实现数据操作。

钩子是什么?

Mongoose 中的钩子机制是指在模型中的文档操作触发特定事件时,执行特定的函数,以便更好地控制数据。钩子可以在插入,更新,删除文档时发生作用,而且它们可以在文档的不同生命周期中执行,比如在保存前,保存后,查询前,查询后等等。这个钩子机制是 Mongoose 的核心功能之一,可以大大简化代码逻辑,提高操作数据的效率和精度。

钩子机制的优点

Mongoose 钩子机制具有以下优点:

  1. 方便快速实现数据预处理:在存储或读取数据之前,先对数据进行处理,例如,计算某个值、进行数据格式转换,或检查数据是否符合要求等;

  2. 异步操作:Mongoose 钩子支持异步操作,例如在发送邮件或短信等场景中,我们可以在文档保存之后抓取到该事件以进行后续操作;

  3. 提升代码健壮性:在写钩子的同时,你可以很容易地处理边缘情况,并且防止出现一些不应该有的数据;

  4. 灵活运用数据:在保存数据之前或之后,你可以对数据进行修改或者一些操作,这可以避免在某些情况下使用难以维护的异步中间件。

钩子分类

Mongoose 钩子按照触发事件的时间可以被分为两类:

  1. 预钩子(pre-hooks):在触发操作前执行。例如,在保存文档之前,你可以对某些字段进行加密、格式化,以及做一些其他预处理的操作。

  2. 后钩子(post-hooks):在触发操作之后执行。例如,在保存文档之后,你可以在数据库中创建一些其他数据,发送某些电子邮件、短信消息等等。

Mongoose 可以为以下文档操作设置预钩子和后钩子:

  1. validate:保存之前验证数据;
  2. save:在保存之前或之后执行某些操作;
  3. remove:在删除一个文档之前或之后执行某些操作;
  4. init:在创建文档实例之后,在执行任何其他操作之前执行的操作;
  5. validate(字段名):验证特定字段之前或之后。

在进行下一步学习之前,需要先在 Node.js 项目中安装 mongoose 库。

--- ------- --------

示例

1、保存前对密码字段加密

首先,我们先看一个常见的使用场景,即在保存账号密码时,对密码进行加密的钩子例子,我们可以通过 Mongoose 提供的 pre-hooks 钩子在保存之前对密码进行加密。我们可以在保存文档之前进行处理,如下:

----- -------- - --------------------
----- ------ - ------------------
----- ---------- - ---

----- ---------- - --- -----------------
    ----- -------
    ------ -------
    --------- ------
---

-- --- ------ ------
---------------------- -------- ------ -
    ----- ---- - -----
    -- ----------------------------- -
        -------------------------- ----- ----- -- -
            -------------------------- ----- ----- ----- -- -
                -- ----- -
                    ------ ----------
                -
                ------------- - -----
                -------
            ---
        ---
    - ---- -
        ------ -------
    -
---

上述代码中,我们创建了一个用户模型,其中包括用户姓名,邮箱和密码,并创建了一个 pre-save 钩子,在保存一个文档之前进行处理if (user.isModified('password')),首先判断密码是否被修改,如果被修改了,将会执行以下两个步骤:

  1. 首先,我们调用 bcryptgenSalt() 函数并生成一个 salt(盐)来进行哈希,salt 的长度是 10 个字符;

  2. 之后,我们调用 bcrypt.hash() 函数来生成哈希,并将结果保存到文档中。

2、在用户保存后发送电子邮件

现在来看一个更加复杂的例子,在用户保存之后,我们会发送一份确认电子邮件给该用户。在使用 Mongoose 后钩子的确切位置之前,您首先需要确信电子邮件服务提供商是否将其称为“异步”运行。

----------------------- -------- ----- -
    -- --------------
    ----- ------ - --------------------
    ----- ------ - --- ---------
    -------------------------------- ---------- -
        ----- --------
    ---
    -- ----
    ----------------- ------------ ----- -- ----------------
---

上述代码定义了一个 post-save 钩子,发送一份确认电子邮件给该用户。在这篇文章中,模运行此代码的整个过程:此用户的帐户将记录在数据库中,确认电子邮件将发送,其中包括名称和电子邮件地址,具有使用后的电子邮件内容。您可以将此覆盖为您选择的电子邮件服务 API。

3、使用 query middleware,过滤被删除的用户

现在来看一下一个过滤已被删除用户的例子,并使用 query middleware 代替实例级别的 middleware。

我们将使用 Mongoose 的 pre-findOneAndDelete(),该方法在文档删除之前运行,并且将运行之前findOneAndDelete()中的查询语句。提前告诉你:这里的代码可能会导致数据丢失,实现必须谨慎审查。

---------------------------------- -------- ------ -
    ----- --- - -----
    ------------------------ ------ --------- ---------------- -- -
        -- --------------- -
            ------ -------
        - ---- -
            ------ -------- --------- ---- ----- ---- ----- ----------------
        -
    -------------- -- -
        ------ ----------
    ---
---

上述代码中,假设代码逻辑中删除操作只在传递使用者的电子邮件字段上完成,我们需要查找该用户,检查我们要删除的模型是否存在,如果模型不存在,则在 findOneAndDelete() 查询中的钩子结束执行,进入到下一个函数。

4、根据用户年龄动态更新其等级

最后我们来看一个计算用户等级的例子。通过使用pre-save钩子,该等级将根据用户的年龄计算,并保存到users 模型中,等级取值在 1 至 10 之间,其中 1 表示是年龄最小的用户。

---------------------- -------- ------ -
    ----- ---- - -----
    -- ------------------------ -
        ----- --- - ---------
        -- ---- -- --- -
            --------- - ---
        - ---- -- ---- - -- -- --- -- --- -
            --------- - --
        - ---- -- ---- - -- -- --- -- --- -
            --------- - --
        - ---- -- ---- - -- -- --- -- --- -
            --------- - --
        - ---- -- ---- - -- -- --- -- --- -
            --------- - --
        - ---- -- ---- - --- -
            --------- - --
        -
    -
    ------ -------
---

结论

在 Mongoose 中使用钩子机制能够对模型中的文档进行快速、方便的操作,自定义逻辑后,钩子机制的复杂性可以方便地起到帮助代码简化和精简操作的作用。在使用钩子机制时还需要注意数据的有效性和正常性,避免出现不必要的错误。钩子机制是一个广泛使用的技术,的确可以帮助您在控制模型数据时更加方便。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/67399834317fbffedf17930c