Перейти к содержанию

Запрос связанных данных

Описание

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

Зачем?

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

Структура вносит несколько идей:

  • Большую часть бизнес-требований по запросу связанных данных можно описать декларативно, а не императивно;
  • Декларативную часть проще вынести в статическую конфигурацию, в которую позже проще вносить изменения;

Вкратце, конфигурация описывает:

  • Какая основная модель данных (SelfModelType);
  • Какие поля для неё запросить (SelfFields);
  • Как эти поля преобразовать (SelfMapper);
  • Какие ограничения добавить к запросу основной модели (Predicates);
  • Какие дополнительные данные запросить параллельно с запросом основном модели (PrefetchTypes);
  • Какие связанные и обратно связанные сущности запросить после основных запросов и каким полям основной модели их присвоить (RelationConfigs);
  • Если требуется, то какие данные и как дозапросить после всех запросов (PostFetch).

Сколько в итоге формируется запросов:

  • Первым набором запросов идут запрос основной модели данных и запросы вспомогательных моделей (небольшие справочники, которые быстро запрашиваются целиком);
  • Вторым набором запросов идут параллельные запросы прямо и обратно связанных сущностей;
  • Если потребуется, третьим и последующими запросами могут дополнительные параллельные запросы прямо и обратно связанных сущностей, конфигурация позволяет это сделать, предоставляя описание таких связей в виде слайса (RelationConfigs[]) — это может быть полезно для запроса связанных сущностей для связанных сущностей основной модели (model -> related -> related);
  • Если потребуется, последним запросом может быть дозапрос дополнительных сущностей, которые невозможно запросить конфигурацией ранее (например, обратно связанные сущности для прямо связанных сущностей основной модели).

Все эти этапы, если содержат параллельные запросы, то дожидаются выполнения самого медленного запроса из параллельных.

На практике чаще всего запускается первые два этапа параллельных запросов, которые суммарно выполняются в интервале от 150мс до 1500мс (на staging-е), в зависимости от количества связей и количества записей в связанных таблицах.

Первый взгляд

Конструктор сборщика принимает два параметра — конфигурацию сборки данных и структуру для работы с БД:

import "domain/internal/data"
import "domain/internal/data/collector/types"

func New(config *types.Config, database data.Database) *Collector { /* ... */ }

Главный публичный метод сборщика называется Collect:

import "github.com/phlx-ru/hatchet/order"

type Collector interface {
    Collect(
        ctx context.Context,
        limit int,
        offset int,
        filter Filter,
        orderBy *order.By,
    ) (items []map[string]any, total int, err error)
}

Параметры limit и offset задают ограничение запроса и сдвиг по элементам, позволяя создать пагинацию — условно:

  • Страница 1 — limit 10, offset 0,
  • Страница 2 — limit 10, offset 10,
  • Страница 3 — limit 10, offset 20

Параметр filter используется для фильтрации данных и описывается в разделе Фильтр.

Параметр orderBy представляет собой экземпляр структуры *order.By, которая содержит в себе объявление поля и направления:

type By struct {
	Desc  bool
	Field string
}
С помощью orderBy можно указать способ сортировки по одному из полей базы данных.

Собирая всё вместе, можно получить нужную сборку данных, если:

  • Сформировать конфигурацию сборки и передать её сборщику;
  • Выполнить метод Collect, передав ему произвольный способ фильтрации и параметры ограничения, сдвига и упорядочивания.

Выше приведена только обзорная информация по работе со сборщиком. Подробности работы — далее.