Metrics (метрики)#
Metrics — декларативное описание логики расчета метрик через фильтры и агрегации.
Три типа метрик#
Counter (базовый) → Uniq (уникальность) → Ratio (производные)
1. Counter — счетчик#
Фильтрует события и суммирует значения:
metric.counter:
metric_name:
filter: [условия] # Опционально
obs: [колонки] # Опционально (по умолчанию 1)
Примеры:
metric.counter:
page_views: {obs: [events_count]}
searches: {filter: [eid: 300], obs: [events_count]}
revenue: {filter: [eid: 500], obs: [price]}
2. Uniq — уникальные#
Считает уникальные значения после применения counter:
metric.uniq:
metric_name:
counter: counter_name
key: [колонки]
thresholds: [число] # Опционально (по умолчанию 0)
Примеры:
metric.uniq:
daily_active_users:
counter: page_views
key: [cookie_id]
power_users:
counter: page_views
key: [cookie_id]
thresholds: [10] # 10+ событий
3. Ratio — производные#
Делит одну метрику на другую:
metric.ratio:
metric_name:
num: числитель
den: знаменатель
Пример:
metric.ratio:
conversion_rate:
num: buyers
den: daily_active_users
Важные особенности:
- Числитель и знаменатель могут быть из разных конфигов и даже разных источников
- Система знает, как посчитать метрики независимо от их расположения
- Best practice: Размещайте ratio-метрику в том же конфиге, где находится числитель, для лучшей организации кода
Filter DSL#
Базовый синтаксис#
filter: [колонка: значение] # Одно условие
filter: [eid: 300, platform_id: 1] # AND
Операторы#
| Оператор | Пример | SQL |
|---|---|---|
= |
eid: 300 |
eid = 300 |
!= |
platform_id.!=: 1 |
platform_id != 1 |
<, >, <=, >= |
price.>=: 1000 |
price >= 1000 |
in |
eid.in: [300, 303] |
eid IN (300, 303) |
!in |
platform_id.!in: [1] |
platform_id NOT IN (1) |
like |
category.like: 'доставка' |
category LIKE 'доставка' |
!like |
category.!like: 'доставка' |
category NOT LIKE 'доставка' |
ilike |
category.ilike: 'доставка' |
category ILIKE 'доставка' |
!ilike |
category.!ilike: 'доставка' |
category NOT ILIKE 'доставка' |
bit |
flags.bit: 3 |
bitwise_and(flags, 3) > 0 |
!bit |
flags.!bit: 3 |
bitwise_and(flags, 3) = 0 |
isnull |
item_id.isnull: false |
item_id IS NOT NULL |
$or |
$or: [cond1, cond2] |
(cond1 OR cond2) |
Примеры#
Сравнения:
filter: [price.<: 1000]
filter: [age.>=: 18]
Сравнение колонок между собой:
filter: [col1.>: $col2] # Символ $ позволяет ссылаться на другое поле
filter: [price.>=: $min_price]
IN:
filter: [eid.in: [300, 303]]
filter: [platform_id.!in: [1]]
LIKE/ILIKE (поиск по строкам):
filter: [category.like: '%доставка%'] # Case-sensitive
filter: [category.ilike: '%ДОСТАВКА%'] # Case-insensitive
filter: [title.!like: '%тест%'] # NOT LIKE
Bitwise:
filter: [item_flags.bit: 5] # Проверка 5-го бита
filter: [item_flags.!bit: 5] # Отсутствие бита
NULL:
filter: [item_id.isnull: false] # IS NOT NULL
Несколько полей в obs:
metric.counter:
total_revenue:
obs: [field_a, field_b, field_c]
# Логика: (сумма по полю А) + (сумма по полю Б) + (сумма по полю С)
OR:
filter:
- vertical_id: 1
- $or:
- [price.>=: 10000]
- [item_flags.bit: 7]
# WHERE vertical_id = 1 AND (price >= 10000 OR (item_flags & (1 << 7)) > 0)
YAML Anchors#
YAML поддерживает переиспользование фильтров через anchors, что помогает избежать дублирования и упрощает поддержку кода.
❌ Без YAML anchors (с дублированием):
metric.counter:
premium_user_payments:
filter:
- is_premium_user: true
- event_id.in: [55, 57, 67]
obs: []
premium_user_refunds:
filter:
- is_premium_user: true # Дублирование!
- event_id.in: [88, 89]
obs: []
✅ С YAML anchors (без дублирования):
definitions:
- &is_premium_user {is_premium_user: true}
- &payment_events {event_id.in: [55, 57, 67]}
- &refund_events {event_id.in: [88, 89]}
metric.counter:
premium_user_payments:
filter: [*is_premium_user, *payment_events]
obs: []
premium_user_refunds:
filter: [*is_premium_user, *refund_events]
obs: []
Более компактный вариант:
definitions:
- &eid_search [300]
- &eid_contact [315]
- &is_search {eid: *eid_search}
- &is_contact {eid: *eid_contact}
metric.counter:
searches: {filter: *is_search}
contacts: {filter: *is_contact}
Примеры#
DAU#
metric.counter:
page_views: {obs: [events_count]}
metric.uniq:
daily_active_users:
counter: page_views
key: [cookie_id]
Конверсионная воронка#
definitions:
- &is_search {eid: 300}
- &is_view {eid: 303}
- &is_contact {eid: 315}
- &is_purchase {eid: 500}
metric.counter:
searches: {filter: *is_search}
views: {filter: *is_view}
contacts: {filter: *is_contact}
purchases: {filter: *is_purchase}
metric.uniq:
searchers: {counter: searches, key: [cookie_id]}
viewers: {counter: views, key: [cookie_id]}
contactors: {counter: contacts, key: [cookie_id]}
buyers: {counter: purchases, key: [cookie_id]}
metric.ratio:
search_to_view_rate: {num: viewers, den: searchers}
view_to_contact_rate: {num: contactors, den: viewers}
contact_to_purchase_rate: {num: buyers, den: contactors}
С enrichments#
# Требует enrichment buyer_segment_cookie
definitions:
- &is_seeker {buyer_segment: 'seeker'}
metric.counter:
seeker_events:
filter: *is_seeker
metric.uniq:
daily_active_seekers:
counter: seeker_events
key: [cookie_id]
Иерархические группировки с key_seq#
key_seq позволяет применять пороги на разных уровнях группировки. Пример: посчитать пользователей, у которых более 10 оплат.
metric.counter:
premium_user_payments:
filter:
- is_premium_user: true
- event_id.in: [55, 57, 67]
obs: []
metric.uniq:
premium_users_with_10plus_payments:
counter: premium_user_payments
key_seq: [user_id, event_id]
thresholds: [10, 0]
Как это работает:
Система генерирует SQL с вложенными группировками, применяя threshold на каждом уровне:
-- Уровень 2: считаем пользователей (threshold = 10)
SELECT SUM(CASE WHEN payments > 10 THEN 1 END) AS num
FROM (
-- Уровень 1: считаем оплаты каждого типа для каждого пользователя (threshold = 0)
SELECT user_id,
SUM(CASE WHEN payments > 0 THEN 1 END) AS payments
FROM (
-- Уровень 0: группируем по user_id и event_id (без threshold)
SELECT user_id,
event_id,
COUNT(*) AS payments
FROM source
WHERE is_premium_user = true AND event_id IN (55, 57, 67)
GROUP BY user_id, event_id
) AS temp1
GROUP BY user_id
) AS temp2
Логика thresholds:
- thresholds: [10, 0] применяется снизу вверх по key_seq: [user_id, event_id]
- Первый threshold (10) применяется к первому ключу (user_id)
- Второй threshold (0) применяется ко второму ключу (event_id)
- На последнем уровне threshold не используется (просто COUNT)
Связь с sources#
# sources.yaml
buyer_stream:
metric_configs: [buyer_stream, conversion] # Рекомендуется
Работа с CLI#
# Генерация SQL для одной метрики
trisigma sl compile --metrics daily_active_users
# Несколько метрик
trisigma sl compile --metrics daily_active_users,buyers,conversion_rate
# Список метрик
trisigma sl list-metrics
# Валидация
trisigma sl validate
Реестр метрик#
После деплоя метрики доступны в Реестре. Там их можно описать, проставить ответственных, теги и тд.
Best Practices#
✅ Рекомендуется#
- Группируйте метрики по предметной области — создавайте отдельные конфиги для разных бизнес-доменов
- Используйте осмысленные названия — договоритесь внутри команды о системе нейминга метрик заранее (например,
{domain}_{action}_{unit}→marketplace_purchases_count). С ростом количества метрик навигация станет сложнее - Используйте YAML anchors — избегайте дублирования фильтров через definitions
- Размещайте ratio-метрики рядом с числителем — если числитель в
buyer_stream.yaml, размещайте ratio там же для лучшей организации
❌ Не рекомендуется#
- Дублирование фильтров — используйте YAML anchors
- Слишком сложные фильтры — вынесите логику в source или enrichment
Следующие шаги#
- Dimensions — разрезы для группировки
- Workflow — практика создания
- Реестр метрик — просмотр SQL
Подробнее: Концепции → Metrics