Мне интересно, есть ли какой-то консенсус относительно того, как лучше всего обрабатывать аргументы полей GraphQL при использовании Dataloader.Пакетная функция batchFn
, в которой нуждается Dataloader, ожидает получения Array<key>
и возвращает Array<Promise>
, и обычно нужно просто вызвать load( parent.id )
, где parent
- это первый параметр преобразователя для данного поля.В большинстве случаев это нормально, но что, если вам нужно предоставить аргументы для вложенного поля?
Например, скажем, у меня есть база данных SQL с таблицами для Users
, Books
и отношениетаблица с именем BooksRead
, представляющая отношение «1: многие» между пользователями: книгами.
Я мог бы выполнить следующий запрос, чтобы увидеть для всех пользователей, какие книги они прочитали:
query {
users {
id
first_name
books_read {
title
author {
name
}
year_published
}
}
}
Допустим, в context
доступно BooksReadLoader
, так что распознаватель для 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
.Есть еще один вопрос SO: Передача аргументов с помощью DataLoader Facebook , но у него нет ответов, и мне трудно найти другие источники, которые попадают в это.
Спасибо!