跳至主要内容

loadMany

类似于 DataLoader 的 load 方法,使用给定的回调函数从您的业务逻辑层读取多个结果。要仅加载一个,请参见 loadOne.

DataLoader 的增强功能

由于 Grafast 中的规划系统,loadOne 可以公开 DataLoader 中不可能的功能。

属性和参数跟踪

一个 loadMany 步骤(技术上是一个 LoadStep)会跟踪通过 .get(attrName) 访问的每个记录的属性名称,以及通过 .setParam(key, value) 设置的任何参数。此信息将传递到您的回调函数,以便您可以更优化地调用您的后端业务逻辑,只检索您需要的数据。

输入/输出等效性

如果您(可选)将 ioEquivalence 参数传递给 loadMany(第二个参数),那么您可以使用它来指示输出中每个记录上的哪个字段与输入等效。这使得可以进行优化,其中链式获取可以在并行执行,如果子项仅依赖于与输入等效的输出。希望一个例子能使这一点更清楚...

想象一下,您正在加载给定组织内的用户

{
usersByOrganizationId(id: Int!) {
id
name
organization {
id
name
}
}
}

您可能拥有以下计划解析器

const plans = {
Query: {
usersByOrganizationId(_, { $id }) {
return loadMany($id, batchGetUsersByOrganizationId);
},
},
User: {
organization($user) {
const $orgId = $user.get("organization_id");
return loadOne($orgId, batchGetOrganizationById);
},
},
};

在当前状态下,系统不知道 $user.get("organization_id") 等效于我们 usersByOrganizationId 字段的 id 参数,因此这将导致链式获取

stateDiagram direction LR state "batchGetUsersByOrganizationId" as A state "batchGetOrganizationById" as B [*] --> A A --> B

但是,我们可以表明 loadMany 步骤的记录的 organization_id 属性($user.get("organization_id"))与其输入($id)等效

 const plans = {
Query: {
usersByOrganizationId(_, { $id }) {
- return loadMany($id, batchGetUsersByOrganizationId);
+ return loadMany($id, 'organization_id', batchGetUsersByOrganizationId);
},
},
User: {
organization($user) {
const $orgId = $user.get("organization_id");
return loadOne($orgId, batchGetOrganizationById);
},
},
};

现在,访问 $user.get("organization_id") 将等效于 usersByOrganizationId 字段的 'id' 参数 - 我们不再需要等待用户加载才能获取他们的组织

stateDiagram direction LR state "batchGetUsersByOrganizationId" as A state "batchGetOrganizationById" as B [*] --> A [*] --> B

用法

基本用法

const $userId = $user.get("id");
const $friendships = loadMany($userId, getFriendshipsByUserIds);

loadMany 接受两个到四个参数,第一个是指定要加载哪些记录的步骤(指定步骤),最后一个是负责加载这些记录的回调函数。

回调函数使用两个参数调用,第一个是来自指定步骤的值列表,第二个是可能影响记录获取的选项。

提示

为了获得最佳效果,我们强烈建议将回调函数定义在公共位置,以便可以重复使用,而不是内联定义。这将允许 LoadManyStep 优化对该函数的调用。

回调函数的示例可能是

const friendshipsByUserIdCallback = (ids, { attributes }) => {
// Your business logic would be called here; e.g. this might be the same
// function that your DataLoaders would call, except we can pass additional
// information to it:
return getFriendshipsByUserIds(ids, { attributes });
};

可选地,倒数第二个参数(3 个参数中的第 2 个,或 4 个参数中的第 3 个)可以指示输入/输出等效性 - 这可以是

  • null 表示没有输入/输出等效性
  • 一个字符串,表示输出上的同名属性等效于整个输入计划
  • 如果该步骤是 list()(或类似)计划,则是一个包含输出上等效于输入中同一项的键列表(或 null 表示没有关系)的数组
  • 如果该步骤是 object()(或类似)计划,则是一个对象,它将对象属性映射到输出中等效于输入上给定项的键。
列表步骤的示例
const $posts = loadMany(
list([$organizationId, $userId]),
["organization_id", "user_id"],
batchGetMemberPostsByOrganizationIdAndUserId,
);
对象步骤的示例
const $posts = loadMany(
list({ oid: $organizationId, uid: $userId }),
{ oid: "organization_id", uid: "user_id" },
batchGetMemberPostsByOrganizationIdAndUserId,
);

高级用法

const $userId = $user.get("id");
const $dbClient = context().get("dbClient");
const $friendships = loadMany($userId, $dbClient, getFriendshipsByUserIds);

除了上面“基本用法”中看到的形式之外,您还可以将第二个步骤传递给 loadMany。此第二个步骤必须是 一元步骤,这意味着它必须表示整个请求中只有一个值(而不是像大多数步骤那样一批值)。由于我们知道它只有一个值,我们可以将其作为单个值传递给回调函数,并且我们的回调函数将能够直接使用它,而无需执行任何手动分组。

这种一元依赖关系对于固定值(例如,来自 GraphQL 字段参数的值)和 GraphQL 上下文上的值(例如,到各种 API 和其他数据源的客户端)很有用。

多个步骤

如果需要将多个步骤的值传递给回调函数,可以使用 list()object() 步骤

const $result = loadMany(list([$a, $b, $c]), callback);

callback 的第一个参数将是一个包含所有计划值元组的数组:ReadonlyArray<[a: AValue, b: BValue, c: CValue]>