Как стать автором
Поиск
Написать публикацию
Обновить

Комментарии 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 лет поняли что он вообще был не нужен.

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

Публикации