Обработка аргументов поля GraphQL с помощью Dataloader?

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

Спасибо!

Кроме того, вам действительно нужен загрузчик данных в этом контексте? Если клиент на самом деле не запрашивает books_read для пользователя такой же более одного раза в одном и том же запросе, нет смысла реализовывать загрузчик данных для этого поля.

Daniel Rearden 23.05.2019 03:11

Привет @DanielRearden Что ты имеешь в виду? В моем примере запроса я предполагаю, что ответ будет массивом ([User]), а не одиночным User. Извините, если я не ясно выразился в своем вопросе. Поскольку запрос предназначен для многих пользователей, я бы предположил, что хочу, чтобы загрузчик данных собирал все user_id, чтобы я мог отправить их в оператор SQL, например SELECT id, first_name FROM User WHERE id IN(?); Поскольку books_read — это поле для каждого отдельного пользователя, а parent для распознавателя — это один User , разве я не хотел бы, чтобы загрузчик данных также пакетировал эти user_ids?

diekunstderfuge 23.05.2019 14:38

Давайте продолжить этот разговор в чате

Daniel Rearden 23.05.2019 16:19
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
3
2 022
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Передайте идентификатор и параметры как один объект в функцию загрузки, примерно так:

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

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

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

Вы также можете передать пользовательский cacheKeyFn или cacheMap в Dataloader, который делает что-то вроде JSON, составляя строку ключа кеша, тогда вам не нужно будет запоминать.

Andrew Ingram 24.05.2019 14:22

хотя это хороший хак, но он ломает набор текста, не совместим с машинописным текстом

doc_id 08.06.2021 15:53

Другие вопросы по теме