Мне интересно, есть ли какой-либо консенсус относительно того, как лучше всего обрабатывать аргументы поля 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
}
}
}
Предположим, что в 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. Есть еще один ТАК вопрос: Передача аргументов с помощью Facebook DataLoader, но на него нет ответов, и мне трудно найти другие источники, которые касаются этого.
Спасибо!
Привет @DanielRearden Что ты имеешь в виду? В моем примере запроса я предполагаю, что ответ будет массивом ([User]), а не одиночным User. Извините, если я не ясно выразился в своем вопросе. Поскольку запрос предназначен для многих пользователей, я бы предположил, что хочу, чтобы загрузчик данных собирал все user_id, чтобы я мог отправить их в оператор SQL, например SELECT id, first_name FROM User WHERE id IN(?); Поскольку books_read — это поле для каждого отдельного пользователя, а parent для распознавателя — это один User , разве я не хотел бы, чтобы загрузчик данных также пакетировал эти user_ids?
Давайте продолжить этот разговор в чате



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Передайте идентификатор и параметры как один объект в функцию загрузки, примерно так:
const UserResolvers = {
books_read: async function getBooksRead( user, args, context ) {
return context.loaders.booksRead.load({id: user.id, ...args});
}
};
Затем позвольте функции пакетной загрузки выяснить, как удовлетворить ее оптимальным образом.
Вы также захотите сделать некоторую мемоизацию для построения объекта, потому что в противном случае кэширование загрузчика данных не будет работать должным образом (я думаю, что оно работает на основе идентичности, а не глубокого равенства).
Вы также можете передать пользовательский cacheKeyFn или cacheMap в Dataloader, который делает что-то вроде JSON, составляя строку ключа кеша, тогда вам не нужно будет запоминать.
хотя это хороший хак, но он ломает набор текста, не совместим с машинописным текстом
Кроме того, вам действительно нужен загрузчик данных в этом контексте? Если клиент на самом деле не запрашивает
books_readдля пользователя такой же более одного раза в одном и том же запросе, нет смысла реализовывать загрузчик данных для этого поля.