在 MongoDB 中,数据可能需要通过多个文档来查询和呈现。按需加载这些数据可以大大提高应用程序的性能,MongoDB 通过 $lookup
操作符支持多文档操作,但也很容易出错。Mongoose 是一个为 Node.js 设计的 MongoDB 对象模型工具,有助于优化这一处理过程。
不过,在 Mongoose 中,有一个名为 Document#populate
的方法,会让人容易迷惑。在这篇文章中,我们将详细分析 Document#populate
,同时提供深度和指导意义。
前期知识准备
在深入 Document#populate
的用法和意义之前,我们需要了解一些前置知识。
Mongoose 的 Schema
在 Mongoose 中,Schema 是一种定义 MongoDB 中文档的结构的指令集。 一个 Mongoose 模型可以通过 Schema 描述一个数据库文档的字段和类型。Schema 有许多选项,例如类型、默认值等。
下面是一个例子的 Schema :
const demoSchema = new Schema({ title: { type: String, required: true }, content: { type: String, required: true }, author: { type: Schema.Types.ObjectId, ref: 'user' }, comments: [{ type: Schema.Types.ObjectId, ref: 'comment' }] })
关系描述
在 Mongoose 中,除了字段之外,Schema 还可以指定包含其他 Schema 的 refs 。对于关系,通常有以下几种类型:
- one-to-one
- one-to-many
- many-to-many
下面是一个例子,表示 user
和 comment
之间的 one-to-many 关系:
-- -------------------- ---- ------- ----- ------------- - --- -------- ----- ------ ----- - ----- ---------------------- ---- ------ - -- ----- ---------- - --- -------- ----- ------- --------- -- ----- ---------------------- ---- --------- -- --
上面代码中的 user
是一个 Schema ,该 Schema 包含一个 comments
数组,该数组引用了许多子 comment
文档。子文档 comment
包含一个 user
字段,该字段引用了父文档 user
。
令人困惑的 Document#populate
在 MongoDB 中,查询一个文档时,通常需要关注该文档的所有引用文档。举个例子,假设我们有一个 article
文档,它包含一个在 user
集合中的作者和一个在 comment
集合中的评论数组。为了从文档中提取引用文档,我们需要使用Mongoose对象模型工具的 populuate()
方法。
Article.findById(id) .populate('author', 'name') .populate('comments', 'text')
在这里,如果我们想知道 article
中的所有评论,那么我们需要调用 populate()
方法,并将目标集合名称 'comments' 作为参数传递。但是,您无法通过调用 populate()
来访问深层嵌套的结构。所以我们需要修改 Schema 的内容。
-- -------------------- ---- ------- ----- ------------- - --- -------- ----- ------- ----- - ----- ---------------------- ---- ------ - -- ----- ---------- - --- -------- ----- ------- --------- -- ----- ---------------------- ---- --------- -- -- ----- ------------- - --- -------- ------ - ----- ------- --------- ---- -- -------- - ----- ------- --------- ---- -- ------- - ----- ---------------------- ---- ------ -- --------- -- ----- ---------------------- ---- --------- -- --
如上面的代码所示,用户和评论之间的关系已被定义,并且已经将之前的评论添加到了文档中,这使得 comments
数组成为了父文档 article
的一部分。现在我们可以使用深层 populate()
查询:
-- -------------------- ---- ------- -------------------- ------------------- ------- --------------------- ------- ----------- ----- ----------- --------- - ----- ------- ------- ------ - --
现在,新的查询将获取嵌套的 user
数据作为评论数组的一部分。
Document#populate()
方法接受两个参数:
path
: 字符串,用于定义需要填充的子文档。select
: 明确表示控制子文档字段。
如果您需要嵌套的弹出子文档,您需要提供一个对象,而不是字符串。每个该对象包含 path
和 select
。
指南
在了解了 populate()
方法和深层结构嵌套之后,我们现在可以为您提供一些有用的技巧以协助您正确地使用 populate()
。
性能问题
弹出数据可以降低应用程序在实时操作时的性能。所以在实践中,最好仅在必要的情况下使用它。 如果您调用 populate()
一个引用元素,则使用传递字符串的方式可能是最好的方法。另外,您还应该选择避免嵌套使用的情况,除非您确定需要它。
空引用的问题
空引用会导致查询速度变慢,查询返回的数据也会更大,建议在开发时避免这种情况。其实,我们每次在创建新的 Schema 与 Model 的时候,都应该将创建好的 Schema 和 Model 与 .pre('deleteMany', deleteManyHandler)
方法连接,为什么? 因为当我们删除一个拥有引用的集合数据将会造成异常,如果我们没有进行异常处理,那么将会造成数据残留问题。
下面是异常处理代码:
-- -------------------- ---- ------- ------------------------------- ---------- ----- ------ ------ -------- ------ - ----- ------- - ---- ---------------- - --------- ----------- -- - ------ - --------- ----------- - -- - ------ ---- -- -------- ----- - -- ----- - ------------------- -- ------ ------- ---- -------- ------ --------- - ------ - - --
每次在删除拥有多对多集合之间的关系时,我们需要执行这个操作。
禁用自动弹出
当然,有时您可能不希望在查询时自动执行嵌套分裂,这通常是在视图中使用时部分文档需要引用的情况下。在这种情况下,您可以通过将对象标记为 _id
添加可用性。
例如:
-- -------------------- ---- ------- ----- ---------- - --- ----------------- --------- ------------------------- ----- ------ -- ----- ---------- - --- ----------------- ----- - ----- ------------------------- ---- ------ -- ----------- - ----- -------- -------- ---- - --
在这里,当您查询 User
时,如果我们使用 populate()
方法填充 viewSchema
中的所有引用,则 someData
将自动弹出。如果只需要 _id
,则可以通过 onlyPopulateId
option 进行控制,否则情况下将返回全部数据。
View.where({ _id: id }) .populate({ path: 'user', select: 'name', options: { onlyPopulateId: true } })
通过上面例子,可以看出这将返回带有用户最少数据的完整视图。
总结
弹出文档可以大大提高有关其嵌套数据的查询效率。在 Mongoose 中,你可以通过 .populate()
实现引用文档的填充,从而在单个查询中轻松地访问嵌套的数据。不过,在使用 populate()
时需要注意一些性能问题和异常处理。希望这些技巧会让你的代码更简洁优雅,协助你在Mongoose中更加高效地工作。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/665587b6d3423812e4a2f8fb