Поиск нескольких элементов в GraphQL Query

Мне интересно, каков наиболее распространенный подход к поиску нескольких элементов на основе идентификатора. По моему наивному пониманию, я вижу 3 варианта:

Опция 1

Принять массив идентификаторов и вернуть все результаты

products(ids: [ID!]!): [Product!]!

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

Вариант 2

Заставить клиентов использовать псевдонимы

product(id: ID!): Product!

Это проще всего реализовать на стороне сервера, а также позволяет клиентам напрямую индексировать результаты на основе идентификатора (при условии, что они используют идентификатор в качестве псевдонима), но также заставляет клиентов создавать более сложные запросы.

Вариант 3

Принимать массив идентификаторов и возвращать результаты с разбивкой на страницы (через шаблон подключения)

products(ids: [ID!]!, after: String, first: Int! = 10): ProductConnection!

Это остается совместимым с другими запросами, которые возвращают результаты с разбивкой на страницы, но также имеет ту же проблему, что и № 1, не позволяя клиентам напрямую индексировать результаты через идентификатор (при условии, что идентификатор используется в качестве псевдонима, как в варианте 2). Шаблон подключения также не сразу интуитивно понятен пользователям, не знакомым с ним.

Есть предложения, основанные на вашем опыте? Спасибо!


person Tim S    schedule 14.09.2019    source источник
comment
Я думаю, что вы охватываете все способы здесь. Я хотел бы упомянуть, что некоторые люди поощряют запросы, созданные для представлений, а не для общих запросов. Кроме того, что мы часто делаем, так это используем вариант 2 и вариант 3 и предлагаем более гибкий параметр фильтра для множественной версии, например. products(filter: { id: { in: [1, 2, 3] } }).   -  person Herku    schedule 14.09.2019
comment
Спасибо за понимание @Herku! Что вы возвращаете во множественном числе? Набор продуктов или связь? Кроме того, используете ли вы какую-либо библиотеку/фреймворк для фильтрации? Используете ли вы другие фильтры, кроме in? Вы называете параметр что-то вроде ProductFilterInput? Цените помощь!   -  person Tim S    schedule 15.09.2019


Ответы (1)


Этот ответ не обязательно представляет дополнительные способы ведения дел, но я подробно расскажу о некоторых лучших методах, которые я нашел для своих схем, и о том, как они относятся к проектам в сообществе.

Вариант 2 не подходит для статических запросов.

Как я уже говорил в комментарии, подумайте о том, что действительно нужно вашему приложению. Если вы хотите искать динамическое количество полей по идентификатору, вариант 2 не будет полезен, если вы хотите использовать статические запросы. Вам нужно будет объединять строки для создания динамических запросов примерно так:

const query = `{ ${ids.map(id => `product${id}: product { ...Frag }\n`).join('')} }`;

Это плохая идея, и в связанной статье рассказывается, почему, а также ваша конечная точка GraphQL, вероятно, не предназначена для этого. Мы могли бы также отправлять несколько запросов один за другим для каждого продукта, но тогда мы теряем одно из ключевых преимуществ GraphQL по сравнению с REST.

Но: мы обычно оставляем поле для запроса отдельных объектов, потому что просмотр одного элемента обычно является вариантом использования нашего приложения, и мы можем очень быстро ответить на этот запрос. См. последний раздел моего ответа на запросы.

Два других варианта в основном одинаковы

Вариант 1 и Вариант 3 следует последовательно выбирать для всего проекта. Соединение может быть бесполезным при запросе идентификаторов, но оно может помешать возврату ко многим элементам и беспрепятственно интегрироваться с Relay.

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

Поэтому, если все ваши запросы, которые возвращают несколько элементов, возвращают соединения, я бы сделал это и здесь. Опасность в том, что запросы станут сложными, и это приводит меня к последней теме.

Общие фильтры считаются вредными

Как правило, рекомендуется отображать только те запросы, которые действительно необходимы вашему внешнему интерфейсу, как описано в этой ветки Twitter. и Ли Байрон (один из создателей) добавляет , что вы также должны показывать только те запросы, которые вы можете выполнять эффективно. В отличие от этого, Apollo рекомендует не соединять клиентские и серверные реализации, а вместо этого абстрактные схемы. Я думаю, вам придется найти баланс.

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

Некоторые проекты в дикой природе предлагают расширенные фильтры, такие как Hasura, Prisma и Гэтсби. Поэтому, если вы хотите создать более общие фильтры, вы можете черпать вдохновение из этих проектов.

person Herku    schedule 18.09.2019
comment
Я хотел бы узнать немного больше о последствиях кэширования на стороне клиента для этих подходов. Похоже, что вариант 2 может работать из коробки (где клиент не повторяет один и тот же запрос, а варианты 1 и 3 всегда должны будут запрашивать весь список, даже если идентификатор отличается? - person Peter Uithoven; 06.12.2019
comment
Ну, теоретически вам не нужно запрашивать весь список, но на практике доступные решения для кэширования недостаточно умны, чтобы справиться с этим. Если это окажется узким местом вашего приложения, вам, вероятно, потребуется специальное решение для этого случая. - person Herku; 10.12.2019