Mongoose 中令人迷惑的 Document#populate

阅读时长 8 分钟读完

在 MongoDB 中,数据可能需要通过多个文档来查询和呈现。按需加载这些数据可以大大提高应用程序的性能,MongoDB 通过 $lookup 操作符支持多文档操作,但也很容易出错。Mongoose 是一个为 Node.js 设计的 MongoDB 对象模型工具,有助于优化这一处理过程。

不过,在 Mongoose 中,有一个名为 Document#populate 的方法,会让人容易迷惑。在这篇文章中,我们将详细分析 Document#populate ,同时提供深度和指导意义。

前期知识准备

在深入 Document#populate 的用法和意义之前,我们需要了解一些前置知识。

Mongoose 的 Schema

在 Mongoose 中,Schema 是一种定义 MongoDB 中文档的结构的指令集。 一个 Mongoose 模型可以通过 Schema 描述一个数据库文档的字段和类型。Schema 有许多选项,例如类型、默认值等。

下面是一个例子的 Schema :

关系描述

在 Mongoose 中,除了字段之外,Schema 还可以指定包含其他 Schema 的 refs 。对于关系,通常有以下几种类型:

  • one-to-one
  • one-to-many
  • many-to-many

下面是一个例子,表示 usercomment 之间的 one-to-many 关系:

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

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

上面代码中的 user 是一个 Schema ,该 Schema 包含一个 comments 数组,该数组引用了许多子 comment 文档。子文档 comment 包含一个 user 字段,该字段引用了父文档 user

令人困惑的 Document#populate

在 MongoDB 中,查询一个文档时,通常需要关注该文档的所有引用文档。举个例子,假设我们有一个 article 文档,它包含一个在 user 集合中的作者和一个在 comment 集合中的评论数组。为了从文档中提取引用文档,我们需要使用Mongoose对象模型工具的 populuate() 方法。

在这里,如果我们想知道 article 中的所有评论,那么我们需要调用 populate() 方法,并将目标集合名称 'comments' 作为参数传递。但是,您无法通过调用 populate() 来访问深层嵌套的结构。所以我们需要修改 Schema 的内容。

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

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

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

如上面的代码所示,用户和评论之间的关系已被定义,并且已经将之前的评论添加到了文档中,这使得 comments 数组成为了父文档 article 的一部分。现在我们可以使用深层 populate() 查询:

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

现在,新的查询将获取嵌套的 user 数据作为评论数组的一部分。

Document#populate() 方法接受两个参数:

  • path: 字符串,用于定义需要填充的子文档。
  • select: 明确表示控制子文档字段。

如果您需要嵌套的弹出子文档,您需要提供一个对象,而不是字符串。每个该对象包含 pathselect

指南

在了解了 populate() 方法和深层结构嵌套之后,我们现在可以为您提供一些有用的技巧以协助您正确地使用 populate()

性能问题

弹出数据可以降低应用程序在实时操作时的性能。所以在实践中,最好仅在必要的情况下使用它。 如果您调用 populate() 一个引用元素,则使用传递字符串的方式可能是最好的方法。另外,您还应该选择避免嵌套使用的情况,除非您确定需要它。

空引用的问题

空引用会导致查询速度变慢,查询返回的数据也会更大,建议在开发时避免这种情况。其实,我们每次在创建新的 Schema 与 Model 的时候,都应该将创建好的 Schema 和 Model 与 .pre('deleteMany', deleteManyHandler) 方法连接,为什么? 因为当我们删除一个拥有引用的集合数据将会造成异常,如果我们没有进行异常处理,那么将会造成数据残留问题。

下面是异常处理代码:

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

每次在删除拥有多对多集合之间的关系时,我们需要执行这个操作。

禁用自动弹出

当然,有时您可能不希望在查询时自动执行嵌套分裂,这通常是在视图中使用时部分文档需要引用的情况下。在这种情况下,您可以通过将对象标记为 _id 添加可用性。

例如:

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

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

在这里,当您查询 User 时,如果我们使用 populate() 方法填充 viewSchema 中的所有引用,则 someData 将自动弹出。如果只需要 _id ,则可以通过 onlyPopulateId option 进行控制,否则情况下将返回全部数据。

通过上面例子,可以看出这将返回带有用户最少数据的完整视图。

总结

弹出文档可以大大提高有关其嵌套数据的查询效率。在 Mongoose 中,你可以通过 .populate() 实现引用文档的填充,从而在单个查询中轻松地访问嵌套的数据。不过,在使用 populate() 时需要注意一些性能问题和异常处理。希望这些技巧会让你的代码更简洁优雅,协助你在Mongoose中更加高效地工作。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/665587b6d3423812e4a2f8fb

纠错
反馈