Как стать автором
Обновить

Комментарии 14

Сколько мук, только чтобы не пользоваться обычным SQL, где этой проблемы нет

Скорее ORM, чем SQL.

Когда есть задача получить данные из БД, а ним ещё данные, а к ним ещё данные, а к ним ещё и всё это соединить на выходе, то без ORM приходится либо грузить всё сразу и передавать в мапперы, либо грузить в процессе маппинга, но можно влетать на N+1, если быть не аккуратным, либо использовать что-то типа дата лоадера, чтобы и грузить в маппинге и в N+1 не попасть.

Без ORM вы сами решаете, что и когда грузить

Зато есть другие увлекательные проблемы :)

К тому, что написано в комментарии, еще можно добавить:

  1. SQL JOIN это декартово произведение, и его размер растет довольно быстро. Начиная с некоторого уровня вложенности в GraphQL, размер Result Set из базы может очень сильно удивить

  2. из той же проблемы есть ряд приседаний связанных с тем, как делать пагинацию. Особенно для вложенных данных.

  3. а еще иногда бывает, что сильно не все данные есть в реляционной базе

В общем на первый взгляд DataLoader выглядит некоторым безумием, а на второй, в парадигме работы с данными GraphQL - весьма практичная штука.

SQL JOIN это не декартово произведение. Декартовым произведением является лишь один вид join - CROSS JOIN

Вероятно речь идёт о том, чтобы одним запросом вытащить все данные. В этом случае, действительно, будет огромный результирующий набор из-за дублирования данных.

Надеюсь никто так и не делает и делает столько запросов, сколько надо.

Я вот мечтаю о чём-то типа GraphQl только для бэкенда, чтобы инфра брала на себя работу по подтаскиванию данных из разных источников. Особенно актуально на каких-нибудь BFF, где, например, нужно превратить каждый ид сотрудника в фио с должностью.

В этом случае, действительно, будет огромный результирующий набор из-за дублирования данных.

А можете раскрыть мысль, что тут имеется ввиду. Почему именно будет огромный набор? Что за дублирование данных? Ведь если мы делаем запрос к БД который вытаскивает ровно те поля, которые запрошены в GraphQL-запросе, то получаем ровно то что и нужно. То есть если сгенерировать SQL запрос 1-к-1 как GraphQL запрос, то это было максимально выгодно. Тогда и никаких DataLoader'ов будет не нужно. Вся сложность как раз в том, чтобы сгенерировать такой запрос.

  1. SQL JOIN это декартово произведение, и его размер растет довольно быстро. Начиная с некоторого уровня вложенности в GraphQL, размер Result Set из базы может очень сильно удивить

Я вот это понял как "сджойнить вообще все таблицы, которые запросили".

Например, запросили документы с их подписантами. У нормальных людей будет 2 запроса: документы и подписанты. А если делать джойном в 1 запрос, то каждый документ вернётся сколько раз в Result Set, сколько у него подписантов.

Выглядит как соломенное чучело.

Операция JOIN в SQL делается на основании условия, то есть это не все кортежи ко всем, это одновременно и объединение и фильтрация.

В Result Set в этом случае будут кортежи в которых частично повторяющиеся данные (это видимо и имелось ввиду под дублированием), но каждый из кортежей в итоге будет уникальным. Там могут складываться ситуации, когда дублирование действительно будет (зависит от схемы БД и запроса), но для этого можно просто DISTINCT к запросу добавить.

Выглядит как соломенное чучело.

И размер этого Result Set будет точно соответствовать сумме Result Set полученных в случае нескольких запросов. Просто представление другое, развернутое в реляционную модель. Другое дело что из ее немного сложнее сделать json с иерархией.

И размер этого Result Set будет точно соответствовать сумме Result Set полученных в случае нескольких запросов

Вот это не понял. Рассуждают так:

Если у нас 100 документов на m полей в каждом 3 подписи на n полей, то джойн вернёт 100*3 строк и n+m колонок.

Если делать 2 запроса, то будет 100 документов на n колонок и максимум 300 подписантов на m колонок.

Во втором случае объём перегоняемых данных меньше, т.к. данные документа не дублируются для каждого подписанта. Т.е. не в точности равны. Или я что-то не понимаю?

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

Я конечно не знаю есть ли такие реализации в природе для реляционных СУБД, просто пытаюсь адекватно рассуждать. Но вот например для MongoDB HotChocolate умеет преобразовывать graphql-запросы в mongo-запросы 1-к-1.

Типичный комментарий от людей, которые не знают как работает резолвинг полей в GraphQL. Все поля грузятся независимо от других, поля в глубине ничего не знают о родительских полях, одним SQL запросом нельзя загрузить данные. Для пагинации в целом джойны это перебор и загрузка лишних данных.

Есть entgo.io, который заточен под графы. Он должен уметь, насколько я понимаю. Но пока не проверял.

Реализации GraphQL для .NET и Java обходят запрос в ширину, по крайней мере насколько я понял. Потому что ну зачем использовать этот хак на промисах и event loop, если можно тоже самое сделать явно, и это будет даже проще.

Шаблон DataLoader - он хорош чтобы батчить тогда, когда у сложно переписать алгоритм, который дёргает какие-нибудь походы в БД или API. Скажем, если бизнес-логика утыкана всякими getUserById, и нет возможности это поменять. Плюс, он вообще возможен только в однопоточной среде (т.е. считай только в ноде).

Отлично эта проблема решилась бы монадами - скажем, если бы в JS, и можно было бы написать свой promise и использовать с ним await. К слову, такое можно сделать на yield-ах.

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории