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
参数,因此这将导致链式获取
但是,我们可以表明 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' 参数 - 我们不再需要等待用户加载才能获取他们的组织
用法
基本用法
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]>
。