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

Enrichments (обогащения)#

Enrichments — автоматическое добавление колонок к источникам данных. Описываются один раз, применяются везде где нужны.

Система enrichments добавляет колонки двумя способами:

  • Табличные enrichments — через LEFT JOIN с внешними таблицами
  • Calculated fields — через скалярные SQL выражения

Зачем?#

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

Система обогащений централизует эту бизнес-логику, позволяя:

  • Переиспользовать SQL-код в любых источниках
  • Быстрее добавлять новые разрезы и поля для метрик
  • Оптимизировать вычисления: система не будет джойнить то, что не нужно в конкретной задаче

Без enrichments:

-- Source A
SELECT e.*, bs.buyer_segment
FROM events e LEFT JOIN buyer_segments bs ON e.cookie_id = bs.cookie_id

-- Source B (тот же JOIN продублирован)
SELECT c.*, bs.buyer_segment
FROM clicks c LEFT JOIN buyer_segments bs ON c.cookie_id = bs.cookie_id

С enrichments:

# enrichments/buyer_segment.yaml - описали один раз
buyer_segment_cookie:
  sql: SELECT cookie_id, buyer_segment FROM dma.v_buyer_segments
  join_key: [cookie_id]

Обогащение будет доступно автоматически во всех источниках с cookie_id.


Табличные Enrichments#

Добавление колонок через LEFT JOIN с внешними таблицами.

Структура#

enrichment_name:
  sql: |
    SELECT join_key, additional_columns
    FROM table
    WHERE {{ pushdown_filter(key='key') }}
        and event_date between :first_date and :last_date
  join_key: [columns]
  default_values:      # Опционально
    column: value

Ключевые механизмы#

1. Автоматическое применение и цепочки#

Enrichment применяется автоматически, если в source или других enrichments есть все колонки из join_key:

buyer_segment_cookie:
  join_key: [cookie_id, event_date]  # Применится если есть cookie_id и event_date

Цепочки enrichments:

Enrichments могут использовать колонки друг друга. Система автоматически определяет порядок через граф зависимостей:

# enrichments/user_profile.yaml
user_profile:
  sql: SELECT user_id, region_id FROM ...
  join_key: [user_id]

# enrichments/region_info.yaml
region_info:
  sql: SELECT region_id, region_name FROM ...  # Использует region_id из user_profile
  join_key: [region_id]

Порядок джоинов определяется автоматически: сначала user_profile, затем region_info.

2. Pushdown Filter#

Макрос для оптимизации — добавляет фильтр внутрь запроса enrichment по уникальным значениям из source:

WHERE {{ pushdown_filter(cookie_id='cookie_id') }}

Генерирует:

WHERE cookie_id IN (SELECT DISTINCT cookie_id FROM source_data WHERE cookie_id IS NOT NULL)

Оптимизация уменьшает объем данных в JOIN в правой таблице, что благосклонно сказывается на времени выполнения и потребляемой памяти.

Синтаксис:

# Один ключ
WHERE {{ pushdown_filter(cookie_id='cookie_id') }}

# Несколько ключей
WHERE {{ pushdown_filter(cookie_id='cookie_id', user_id='user_id') }}

Имя аргумента - это называние поля из join_key, а значение - это колонка, которая существует в контексте sql-запроса.

3. Default Values#

Заполняет NULL через COALESCE:

traffic_source_cookie:
  sql: SELECT cookie_id, source_name, is_paid FROM ...
  join_key: [cookie_id]
  default_values:
    source_name: 'organic'
    is_paid: false

4. Параметр гранулярности#

Enrichment может зависеть от гранулярности расчета :granularity. Например, для расчетов m42 с месячной гранулярностью.

weekly_user_activity:
  sql: |
    SELECT
      user_id,
      DATE_TRUNC(:granularity, event_date) as metric_date,
      AVG(activity_score) as avg_activity
    FROM dma.user_activity
    WHERE {{ pushdown_filter(user_id='user_id') }}
    GROUP BY user_id, DATE_TRUNC(:granularity, event_date)
  join_key: [user_id, metric_date]

Система подставит правильное значение гранулярности ('day', 'week', 'month') в зависимости от требований расчета.

Важно! Если используется granularity, то колонка с датой должна называться metric_date — зарезервированное имя специально для этих целей.

Best Practices для табличных enrichments#

  • Всегда используйте pushdown_filter для больших таблиц
  • Фильтруйте по датам: AND event_date BETWEEN :first_date AND :last_date
  • Проверяйте уникальность результатов sql по join_key - важно для отсутствия дублей!

Calculated Fields#

Добавление колонок через скалярные SQL выражения, вычисляемые на лету из существующих полей.

Важно: Calculated fields могут быть только скалярными выражениями:

✅ Разрешено:

  • Арифметика, CASE, COALESCE, функции дат и строк

❌ Запрещено:

  • Агрегации (SUM, COUNT, AVG и т.д.)
  • Оконные функции (ROW_NUMBER, RANK и т.д.)

Зачем?#

Calculated fields решают две задачи:

1. Объединение полей из разных enrichments

Когда нужно составить новое поле, используя поля из разных обогащений:

# enrichments/infmquery.yaml
logical_category:
  sql: SELECT logcat_id, logical_category_id as logical_category_id_logcat FROM ...
  join_key: [logcat_id]

microcategory:
  sql: SELECT microcat_id, logical_category_id as logical_category_id_microcat FROM ...
  join_key: [microcat_id]

calculated_fields:
  # Объединяем в единое поле
  logical_category_id: coalesce(logical_category_id_logcat, logical_category_id_microcat)
  vertical_id: coalesce(vertical_id_logcat, vertical_id_microcat)

Теперь logical_category_id можно использовать:

  • В metrics как обычное поле
  • В других enrichments как join_key
  • В dimensions

2. Переиспользование часто используемых скалярных выражений

Вместо дублирования вычислений в каждой метрике:

item_attr_log:
  sql: SELECT item_id, event_date, sort_time, start_time, price FROM ...
  join_key: [item_id, event_date]

calculated_fields:
  # Один раз описали вычисление
  item_age_hours: date_diff('hour', sort_time, event_datetime)
  price_category:
    case
    when price >= 100000 then 'expensive'
    when price >= 10000 then 'medium'
    else 'cheap'
    end

Базовый синтаксис#

# В YAML файле enrichment или source
calculated_fields:
  field_name: 'SQL выражение'

Пример:

item_attr_log:
  sql: |
    SELECT item_id, event_date, sort_time, start_time, price, event_datetime
    FROM dma.item_attr_log
    WHERE {{ pushdown_filter(item_id='item_id') }}
  join_key: [item_id, event_date]

calculated_fields:
  item_age_hours: date_diff('hour', sort_time, event_datetime)
  item_start_hours: date_diff('hour', start_time, event_datetime)


Многострочные выражения#

CASE выражения:

calculated_fields:
  price_bin: |
    case
    when price >= 5000000 then 5000000
    when price >= 3000000 then 3000000
    when price >= 1000000 then 1000000
    when price >= 500000 then 500000
    when price >= 100000 then 100000
    when price >= 10000 then 10000
    when price >= 0 then 0
    end


Цепочки приоритетов (Fallback)#

Несколько выражений для одного поля с приоритетом:

calculated_fields:
  user_segment_market:
    - coalesce(user_segment_market_joined, user_segment_market_default)    # Приоритет 1
    - user_segment_market_joined                                           # Приоритет 2
    - user_segment_market_default                                          # Приоритет 3

Система попытается использовать первое доступное выражение.


Валидация#

Разрешено:

  • Алфавит, цифры, подчеркивание, дефис: field_name, field-name, field123
  • Многострочные выражения
  • Зависимости от других calculated fields

Запрещено:

  • Имена, начинающиеся с цифры: 123field
  • Пробелы в именах: field name
  • Циклические зависимости: field_a: field_b, field_b: field_a
  • Самозависимость: field_a: field_a * 2

Порядок резолвинга колонок#

Когда в запросе используется колонка, система ищет её в следующем порядке:

1. Поиск в source#

Сначала проверяется, есть ли колонка в source. Если да — берётся оттуда.

2. Поиск в enrichments#

Если колонки нет в source, проверяются enrichments (табличные и calculated) в порядке их определения в YAML файле.

Берётся первый enrichment, у которого: - Есть нужная колонка - join_key можно применить (все колонки из join_key доступны)

3. Зависимости между enrichments#

Enrichments могут зависеть друг от друга:

  • Табличный enrichment может использовать колонку из другого табличного enrichment
  • Calculated field может использовать колонки из табличных enrichments
  • Табличный enrichment может использовать calculated field как join_key

Система автоматически определяет порядок применения через граф зависимостей.

Пример:

# Сначала получаем категорию
logical_category:
  sql: SELECT item_id, logical_category_id FROM dma.categories
  join_key: [item_id]

# Calculated field использует колонку из enrichment
calculated_fields:
  is_auto: logical_category_id = 9  # Авто

# Enrichment использует calculated field как join_key
auto_attributes:
  sql: SELECT item_id, brand, model FROM dma.auto_attributes
  join_key: [item_id, is_auto]  # Применится только для авто

Валидация:

  • ❌ Циклические зависимости запрещены
  • ❌ Табличный enrichment и calculated field не могут возвращать колонку с одинаковым именем

Работа с CLI#

# Проверка применения enrichments
trisigma sl compile --source buyer_stream --dimensions category,region

# Валидация
trisigma sl validate

# Просмотр финального SQL
trisigma sl compile --metrics your_metric --dimensions your_dim

Следующие шаги#

  • Metrics — использование полей из enrichments и calculated fields
  • Workflow — практика создания

Подробнее: Концепции → Enrichments