обработка аргументов поля GraphQL с помощью Dataloader? - PullRequest
1 голос
/ 23 мая 2019

Мне интересно, есть ли какой-то консенсус относительно того, как лучше всего обрабатывать аргументы полей 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 , но у него нет ответов, и мне трудно найти другие источники, которые попадают в это.

Спасибо!

1 Ответ

0 голосов
/ 24 мая 2019

Передайте id и params как один объект функции загрузки, что-то вроде этого:

const UserResolvers = {
  books_read: async function getBooksRead( user, args, context ) {
    return context.loaders.booksRead.load({id: user.id, ...args});
  }
};

Затем позвольте функции пакетной загрузки выяснить, как ее удовлетворить оптимальным образом.

Вы также захотите сделать некоторые заметки для построения объекта, потому что в противном случае кэширование загрузчика данных не будет работать должным образом (я думаю, что оно работает на основе идентичности, а не глубокого равенства).

...