使用Facebook的DataLoader传递参数

Sim*_*mon 12 javascript graphql graphql-js

我正在使用DataLoader将请求/查询一起批处理.在我的加载器功能中,我需要知道所请求的字段,以避免有一个SELECT * FROM query而是SELECT field1, field2, ... FROM query...

使用DataLoader传递resolveInfo所需的最佳方法是什么?(我resolveInfo.fieldNodes用来获取请求的字段)

目前,我正在做这样的事情:

await someDataLoader.load({ ids, args, context, info });
Run Code Online (Sandbox Code Playgroud)

然后在实际的loaderFn中:

const loadFn = async options => {
const ids = [];
let args;
let context;
let info;
options.forEach(a => {
    ids.push(a.ids);
    if (!args && !context && !info) {
        args = a.args;
        context = a.context;
        info = a.info;
    }
});

return Promise.resolve(await new DataProvider().get({ ...args, ids}, context, info));};
Run Code Online (Sandbox Code Playgroud)

但正如你所看到的那样,这很黑,而且感觉并不好......

有谁知道我怎么能做到这一点?

Her*_*rku 14

我不确定这个问题是否有一个很好的答案,因为 Dataloader 不是为这个用例制作的,但我与 Dataloader 进行了广泛的合作,编写了类似的实现并探索了其他编程语言的类似概念。

让我们了解为什么 Dataloader 不是为这个用例制作的,以及我们如何仍然使它工作(大致就像你的例子一样)。

Dataloader 不是用于获取字段的子集

Dataloader 是为简单的键值查找而设计的。这意味着给定一个像 ID 这样的,它会在它后面加载一个值。为此,它假定 ID 后面的对象将始终相同,直到它失效。这是启用数据加载器功能的单一假设。没有它,Dataloader的三个关键功能将不再起作用:

  1. 批处理请求(多个请求在一个查询中一起完成)
  2. 重复数据删除(对同一个键的请求两次导致一次查询)
  3. 缓存(同一键的连续请求不会导致多个查询)

如果我们想最大限度地发挥 Dataloader 的功能,这将引导我们遵循以下两个重要规则:

两个不同的实体不能共享同一个 key,否则我们可能会返回错误的实体。这听起来微不足道,但它不在您的示例中。假设我们要加载具有 ID1和字段的用户idname。稍后(或同时)我们想用 ID1和字段id以及email. 从技术上讲,它们是两个不同的实体,它们需要具有不同的密钥。

同一个实体应该始终拥有相同的密钥。再次听起来微不足道,但实际上不在示例中。用户ID为1和字段idname应该是相同的,与用户ID1和字段nameid(通知的顺序)。

简而言之,密钥需要具有唯一标识实体所需的所有信息,但仅此而已

那么我们如何将字段传递给 Dataloader

await someDataLoader.load({ ids, args, context, info });
Run Code Online (Sandbox Code Playgroud)

在您的问题中,您已向 Dataloader 提供了更多内容作为关键。首先,我不会将 args 和 context 放入键中。当上下文发生变化时,您的实体是否会发生变化(例如,您现在正在查询不同的数据库)?可能是的,但是你想在你的数据加载器实现中考虑到这一点吗?相反,我建议为每个请求创建新的数据加载器,如文档中所述。

整个请求信息应该在密钥中吗?不,但我们需要请求的字段。除此之外,您提供的实现是错误的,并且在使用两个不同的解析信息调用加载程序时会中断。您只设置了第一次调用的解析信息,但实际上每个对象的解析信息可能不同(想想上面的第一个用户示例)。最终我们可以实现以下数据加载器的实现:

await someDataLoader.load({ ids, args, context, info });
Run Code Online (Sandbox Code Playgroud)

这是一个可靠的实现,但它有一些弱点。首先,如果我们在同一个批处理请求中有不同的字段需求,我们就会过度获取很多字段。其次,如果我们1[id,name]从缓存键函数中获取了一个带有键的实体,我们也可以回答(至少在 JavaScript 中)键1[id]1[name]该对象。在这里,我们可以构建一个可以提供给 Dataloader 的自定义地图实现。知道关于我们的缓存的这些事情就足够聪明了。

结论

我们看到这确实是一个复杂的问题。我知道它经常被列为 GraphQL 的一个好处,即您不必为每个查询从数据库中获取所有字段,但事实是,实际上这很少值得麻烦。不要优化不慢的东西。甚至是慢,是瓶颈吗?

我的建议是:编写简单的 Dataloaders 来简单地获取所有(需要的)字段。如果您有一个客户端,很可能对于大多数实体,客户端无论如何都会获取所有字段,否则它们将不会成为您 API 的一部分,对吗?然后使用诸如查询内省之类的东西来衡量慢查询,然后找出到底哪个字段是慢的。然后您只优化缓慢的事情(例如,请参阅我在此处优化单个用例的答案)。如果您是大型电子商务平台,请不要为此使用 Dataloader。构建更智能的东西,不要使用 JavaScript。

  • 您能解释一下“构建更智能的东西”是什么意思吗?使用数据加载器不聪明吗?反对 JavaScript 的论据是什么?它似乎相当稳定、足够快,并且有一个很大的社区。 (3认同)