GraphQL Dataloader 事先不知道键

Posted

技术标签:

【中文标题】GraphQL Dataloader 事先不知道键【英文标题】:GraphQL Dataloader not knowing keys in advance 【发布时间】:2018-07-28 20:09:48 【问题描述】:

Dataloader 能够批处理和缓存请求,但它只能通过调用 load(key) 或 loadMany(keys) 来使用。

我遇到的问题是,有时我不知道我要提前加载的项目的键。

我正在使用 sql 数据库,当当前对象具有来自与另一个模型的 belongsTo 关系的外键时,这可以正常工作。

例如,某个用户属于某个组,因此有一个 groupId。要解析组,您只需调用 groupLoader.load(groupId)。

另一方面,如果我想解析一个组中的用户,其中可能有很多,我想要一个查询,例如

SELECT * from users where user.groupId = theParticularGroupId

但是这样的查询不使用用户的密钥,所以我不确定如何使用数据加载器。

我可以做另一个请求来获取密钥,例如

SELECT id from users where user.groupId = theParticularGroupId

然后使用这些键调用 loadMany...但我可以直接请求数据。

我注意到 dataloader 有一个 prime(key, value) 函数可以用来初始化缓存,但是只有在数据已经获取后才能完成。此时可能已经发送了许多查询,并且可能已获取重复数据。


另一个例子是下面的查询

query 
  groups(limit: 10) 
    id
    ...
    users 
      id
      name
      ...
    
  

如果我正在搜索前 10 个组或最后 10 个组,我无法知道密钥。然后一旦我有了这 10 个组。我不知道他们用户的密钥,以及每个解析器是否会使用诸如

之类的查询来解析用户
SELECT * from users where user.groupId = theParticularGroupId

该查询将被执行 10 次。加载数据后,我现在可以启动缓存,但已经发出了 10 个请求。

有没有办法解决这个问题?也许不同的模式或数据库结构或者数据加载器甚至都不是正确的解决方案。

【问题讨论】:

【参考方案1】:

您需要一个数据加载器实例来进行查找,在这种情况下,您有一个组 ID,并且您需要用户:

import DataLoader from 'dataloader';

const userIdsForGroupLoader = new DataLoader(groupIds => batchGetUsersIdsForGroups(groupIds));

现在您的batchGetUsersForGroups 函数本质上必须将一组组 ID 转换为一组用户数组(每个组一个用户 ID 数组)。

您将从 IN 查询开始:

SELECT id from users where user.groupId in (...groupIds)

这将为您提供一个单一的用户结果集,您必须通过他们的groupId 对它们进行分组来对其进行操作,该数组应根据groupIds 的原始数组进行排序。确保为没有任何用户的 groupId 返回一个空数组。

请注意,在此我们只返回用户 ID,但您可以在拥有用户后一次性批量获取用户。您可以稍微调整一下以返回用户自己,您必须自己决定这是否是正确的方法。

我在this article 中提到的所有内容都可以通过巧妙地使用 Dataloader 来实现。但关键的一点是,您传递给 load/loadMany 函数的值不必与您尝试返回的对象的 ID 相对应。

【讨论】:

这是否意味着即使加载相同的实体,您也必须为每种加载数据的方式创建许多不同的数据加载器?就像您文章中的评论一样,您建议使用诸如loader.load( id, first, last, after, before, filters) 之类的加载器来批处理连接请求。但是,您还必须使用返回的数据来启动其他数据加载器,这些数据加载器可能会通过不同的签名请求相同类型的数据,这取决于客户端查询的字段可能会或可能不会使用。这似乎很快就会变得难以管理? 我猜如果您不启动其他数据加载器,您只会错过一些缓存,但仍然可以获得批处理效果,这是主要的好处。这可能会大大降低复杂性。 所以在这个例子中,usersForGroupLoader 可以返回用户,这意味着你有足够的数据来初始化一个普通的旧 usersByIdLoader。最终,最佳策略取决于底层数据获取模式和最常见查询模式的组合。在我为 (depop.com) 执行此操作的站点中,核心实体的数量相对较少(用户、产品、喜欢、书签、cmets、对话、消息和其他一些),而且我基本上每次查找都有一个加载器-type,这往往意味着一个加载器用于一个实体-by-id,另一个用于每个主要连接。 我承认它确实有点难以管理,负责创建所有加载器并协调它们之间的缓存的模块很容易成为代码库中最复杂的部分。但这是我的第一次尝试,所以我相信如果有更多的经验,它可以做得更优雅。

以上是关于GraphQL Dataloader 事先不知道键的主要内容,如果未能解决你的问题,请参考以下文章

GraphQL,Dataloader,[ORM or not],有很多关系理解

在 GraphQL 服务器设置中何时使用 Redis 以及何时使用 DataLoader

为GraphQL Server自动生成DataLoader!

Apollo GraphQL DataLoader DynamoDb

初始化 Graphql 应用程序时出现 org.dataloader.DataLoaderRegistry 错误

graphql dataloader 无法读取未定义错误的属性“加载”