我想知道是否在使用Dataloader时如何最好地处理GraphQL字段参数方面存在共识。 Dataloader需要的batchFn
批处理函数预期会收到Array<key>
并返回一个Array<Promise>
,通常只需调用load( parent.id )
,其中parent
是第一个参数。给定字段的解析器。在大多数情况下,这很好,但是如果您需要为嵌套字段提供参数呢?
例如,假设我有一个SQL数据库,其中包含Users
,Books
的表,以及一个名为BooksRead
的关系表,它们表示Users:Books之间的1:许多关系。 / p>
我可能会运行以下查询,以便为所有用户查看他们阅读过哪些书:
query {
users {
id
first_name
books_read {
title
author {
name
}
year_published
}
}
}
让我们说BooksReadLoader
中有一个context
可用,因此books_read
的解析器可能看起来像这样:
const UserResolvers = {
books_read: async function getBooksRead( user, args, context ) {
return await context.loaders.booksRead.load( user.id );
}
};
BooksReadLoader
的批处理加载函数将对数据访问层方法进行async
调用,该方法将运行一些SQL,如:
SELECT B.* FROM Books B INNER JOIN BooksRead BR ON B.id = BR.book_id WHERE BR.user_id IN(?);
我们将从结果行中创建一些Book
实例,并按user_id
分组,然后返回keys.map(fn)
以确保我们为每个user_id
键分配正确的书本加载程序的缓存。
现在假设我向books_read
添加一个参数,询问用户已阅读的所有1950年以前出版的书:
query {
users {
id
first_name
books_read(published_before: 1950) {
title
author {
name
}
year_published
}
}
}
理论上,我们可以运行相同的SQL语句,并在解析器中处理参数:
const UserResolvers = {
books_read: async function getBooksRead( user, args, context ) {
const books_read = await context.loaders.booksRead.load( user.id );
return books_read.filter( function ( book ) {
return book.year_published < args.published_before;
});
}
};
但是,这并不理想,因为当实际上只有少数几行实际上满足该参数时,我们仍然从Books
表中获取潜在的大量行。相反,执行此SQL语句会更好:
SELECT B.* FROM Books B INNER JOIN BooksRead BR ON B.id = BR.book_id WHERE BR.user_id IN(?) AND B.year_published < ?;
我的问题是,通过cacheKeyFn
可用的new DataLoader( batchFn[, options] )
选项是否允许将字段的参数向下传递以在数据访问层中构造动态SQL语句?我已经审查过https://github.com/graphql/dataloader/issues/75,但仍不清楚cacheKeyFn
是否是可行的方法。我正在使用apollo-server-express
。还有另外一个问题:Passing down arguments using Facebook's DataLoader,但没有答案,我很难找到其他来源。
谢谢!
答案 0 :(得分:0)
将id和params作为单个对象传递给load函数,如下所示:
const UserResolvers = {
books_read: async function getBooksRead( user, args, context ) {
return context.loaders.booksRead.load({id: user.id, ...args});
}
};
然后让批处理加载函数找出如何以最佳方式满足它。
您还需要对对象的构造做一些说明,因为否则,数据加载器的缓存将无法正常工作(我认为它是基于身份而不是深度平等的)。