数据加载器
n+1 问题是在一个查询中请求多种类型的数据,但需要 n 个请求而不是一个。这通常在数据嵌套时遇到,例如如果你请求作者及其书籍的名称。这是 GraphQL API 固有的问题,可以通过将多个请求批量合并为一个请求来解决。这可以通过数据加载器库自动执行,该库将合并在单个执行框架(事件循环的单个滴答)内发生的所有单独加载,然后使用所有请求的键调用批处理函数。这意味着为每个数据库调用编写一个批量加载函数,将多个查询聚合为一个查询,并过滤结果以将它们重新分配给原始查询。幸运的是,MikroORM 有大量元数据可以透明地自动化此过程,因此你不必编写自己的批量加载函数。
¥The n+1 problem is when multiple types of data are requested in one query, but where n requests are required instead of just one. This is typically encountered when data is nested, such as if you were requesting authors and the name of their books. It is an inherent problem of GraphQL APIs and can be solved by batching multiple requests into a single one. This can be automated via the dataloader library which will coalesce all individual loads which occur within a single frame of execution (a single tick of the event loop) and then call your batch function with all requested keys. That means writing a batch loading function for every db call that aggregates multiple queries into a single one, plus filtering the results to reassign them to the original queries. Fortunately, MikroORM has plenty of metadata to transparently automate this process so that you won't have to write your own batch loading functions.
在当前版本中,MikroORM 能够自动批处理 references 和 collections。但是,如果你有兴趣尝试一下,有一个 树库之外 可以负责使用受支持运算符子集对整个查找查询进行批处理。
¥In the current version, MikroORM is able to automatically batch references and collections. However, if you are interested to try it out there is an out of tree library which takes care of batching whole find queries with a subset of the operators supported.
用法
¥Usage
默认情况下禁用数据加载器,但可以轻松地全局启用它们:
¥Dataloaders are disabled by default, but they can be easily enabled globally:
import { DataloaderType } from '@mikro-orm/core';
MikroORM.init({
dataloader: DataloaderType.ALL,
});
DataloaderType.REFERENCE
为 参考 启用数据加载器,DataloaderType.COLLECTION
为 集合 启用数据加载器,而 DataloaderType.ALL
为两者启用数据加载器。还支持布尔值来启用/禁用所有这些。
¥DataloaderType.REFERENCE
enables the dataloader for References, DataloaderType.COLLECTION
enables it for Collections while DataloaderType.ALL
enables it for both. A boolean value is also supported to enable/disable all of them.
数据加载器也可以通过 Reference
或 Collection
类的 load()
方法选项按查询启用。
¥The dataloader can also be enabled per-query via the load()
method options of Reference
or Collection
class.
Reference
属性
¥Reference
properties
ManyToOne 和 OneToOne 关系需要使用 参考封装器:
¥ManyToOne and OneToOne relations require the use of the Reference wrapper:
@ManyToOne(() => Book, { ref: true })
book!: Ref<Book>;
或者,你可以动态创建引用实例:
¥Alternatively, you can create the reference instance dynamically:
-book.author.load({ dataloader: true }); // can be also enabled globally
+wrap(book.author).toReference().load({ dataloader: true });
带有 Promise.all()
的示例
¥Example with Promise.all()
以下将发出两个 SQL 语句。一个用于加载作者,另一个用于加载属于这些作者的所有书籍:
¥The following will issue two SQL statements. One to load the authors and another one to load all the books belonging to these authors:
const authors = await orm.em.find(Author, [1, 2, 3]);
await Promise.all(authors.map(author => author.books.load({ dataloader: true })));
这是另一个使用数据加载器通过单个查询检索多个引用的示例:
¥This is another example where the dataloader is being used to retrieve multiple references via a single query:
const books = await orm.em.find(Book, [1, 2, 3]);
await Promise.all(books.map(book => book.author.load({ dataloader: true })));
GraphQL
如果你使用 GraphQL,则不必使用 Promise.all
,只需确保在解析器中使用 Reference.load()
和 Collection.load()
方法即可。发出正常查询就足够了:
¥If you're using GraphQL you won't have to use Promise.all
, just make sure to use the Reference.load()
and Collection.load()
methods in your resolvers. Issuing a normal query would be enough:
{
authors {
name
books {
title
}
}
}
假设你已启用数据加载器,MikroORM 将合并在单个执行框架(事件循环的单个滴答)内发生的所有单独加载并自动对它们进行批处理。
¥Assuming you've enabled the dataloaders, MikroORM will coalesce all individual loads which occur within a single frame of execution (a single tick of the event loop) and automatically batch them.