- Зачем: - конвенция #63 записана, а код её не достиг: колонки времени стояли без пояса, и сходилось всё лишь потому, что пояс сервера — UTC. - Что: - UTCEventTime объявлен DateTime('UTC'), служебные метки _load_ts и kafka_timestamp — DateTime64(3, 'UTC') в STG и ODS. - parseDateTimeOrNull получил третьим аргументом 'UTC': маска сверяет суффикс Z как букву, зоны из строки не берёт вовсе. - контракт схемы и описание выгрузки несут тип с поясом; имя пояса Europe/Samara встало рядом со смещением в world.py, сходимость сверяет тест. - учебный комментарий о линзе — у первой колонки с явным поясом. - Проверка: - make lint, make typecheck, make test (408 тестов) - make clean && make up && make check-clickhouse — 9 из 9 - замер тикета повторён: под session_timezone='Europe/Samara' колонка показана 2026-05-31 23:37:00, как и без настроек Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
10 KiB
Описание выгрузки: событие кликстрима
Документ собран из контракта схемы генератора
(generator/src/clickstream_generator/schema.py). Руками не править —
пересобрать: make docs.
Одно событие — одна строка: хит по образцу облачной выгрузки Яндекс Метрики.
Многозначное лежит в параллельных массивах, плюс одно сырое JSON-поле
ecommerce. Длина у массивов общая внутри группы, а не по всему
событию: purchase* — по элементу на заказ (у нас всегда один), product* —
по элементу на товар. Отдельной сущности «визит» в выгрузке нет — визиты
собирают на стороне хранилища, а VisitID дан как эталон для самопроверки.
Имена и типы колонок — стороны источника. Хранилище принимает их как есть и нормализует у себя: то же имя в нашем стиле ждёт в столбце «Нормализованное имя». Это имя источника, приведённое к snake_case, а не имя атрибута в модели данных: слой DDS складывает свою модель и называет атрибуты по ней. Столбец «Тип numpy» показывает, чем колонка представлена внутри генератора; у массивов это тип элемента. Номер — место колонки в выгрузке: порядок задан контрактом.
Колонки группы «Ecommerce» заполнены только у торговых событий:
add_to_cart несёт один товар, purchase — состав заказа и блок
purchase*. У остальных событий они пусты.
Деньги: productPrice — целые рубли, округление формата. Точная сумма
заказа живёт в purchaseRevenue и в сыром ecommerce, поэтому пересчитать
выручку по разобранным массивам нельзя — цены каталога бывают с копейками.
Всего колонок: 47.
Идентификаторы и время
| № | Колонка | Тип ClickHouse | Тип numpy | Нормализованное имя | Комментарий |
|---|---|---|---|---|---|
| 1 | WatchID |
UInt64 |
uint64 |
watch_id |
id события — хита; держится ниже 2^53, выше числа в JSON округляются |
| 2 | VisitID |
UInt64 |
uint64 |
visit_id |
id визита от генератора — эталон лабы: собери сессии сам и сравни |
| 3 | ClientID |
UInt64 |
uint64 |
client_id |
анонимный id браузера — кука; по хешу от неё таблица шардируется |
| 4 | CounterID |
UInt32 |
uint32 |
counter_id |
id счётчика: на стенде константа, сайт один |
| 5 | EventDate |
Date |
datetime64[D] |
event_date |
дата события в часовом поясе счётчика; по ней режется партиция. Дату из UTCEventTime не выводить: у ночных событий она на сутки другая |
| 6 | UTCEventTime |
DateTime('UTC') |
datetime64[s] |
utc_event_time |
время события в UTC — единственная метка времени, как у Метрики; сутки же считаются в поясе счётчика, поэтому toDate(UTCEventTime) ≠ EventDate |
| 7 | ClientTimeZone |
Int16 |
int16 |
client_timezone |
смещение часового пояса клиента от UTC, в минутах |
| 8 | EventType |
LowCardinality(String) |
object |
event_type |
тип события: pageview, add_to_cart, purchase — добавка стенда, у Метрики такого поля нет |
| 9 | Sign |
Int8 |
int8 |
sign |
всегда 1: колонка формата, исправлений записей генератор не шлёт |
Страница и атрибуция
| № | Колонка | Тип ClickHouse | Тип numpy | Нормализованное имя | Комментарий |
|---|---|---|---|---|---|
| 10 | URL |
String |
object |
url |
адрес страницы события |
| 11 | Referer |
String |
object |
referer |
адрес, с которого посетитель пришёл на страницу |
| 12 | Title |
String |
object |
title |
заголовок страницы |
| 13 | UTMSource |
String |
object |
utm_source |
метка utm_source: площадка перехода |
| 14 | UTMMedium |
String |
object |
utm_medium |
метка utm_medium: тип трафика |
| 15 | UTMCampaign |
String |
object |
utm_campaign |
метка utm_campaign: рекламная кампания |
| 16 | UTMContent |
String |
object |
utm_content |
метка utm_content: что различает объявления одной кампании |
| 17 | UTMTerm |
String |
object |
utm_term |
метка utm_term: ключевое слово перехода |
| 18 | LastTrafficSource |
String |
object |
last_traffic_source |
последний источник трафика: organic, direct, ad и подобные |
| 19 | HasGCLID |
UInt8 |
uint8 |
has_gclid |
1, если в адресе была метка Google Ads |
| 20 | YCLID |
UInt64 |
uint64 |
yclid |
id клика Яндекс Директа; без метки — 0 |
Браузер, устройство, гео
| № | Колонка | Тип ClickHouse | Тип numpy | Нормализованное имя | Комментарий |
|---|---|---|---|---|---|
| 21 | Browser |
String |
object |
browser |
браузер посетителя |
| 22 | BrowserMajorVersion |
UInt16 |
uint16 |
browser_major_version |
старшая версия браузера |
| 23 | BrowserLanguage |
String |
object |
browser_language |
язык браузера |
| 24 | OperatingSystem |
String |
object |
operating_system |
операционная система с версией |
| 25 | OperatingSystemRoot |
String |
object |
operating_system_root |
семейство операционной системы, без версии |
| 26 | DeviceCategory |
UInt8 |
uint8 |
device_category |
тип устройства кодами Метрики: 1 — десктоп, 2 — телефон, 3 — планшет, 4 — телевизор; у Метрики это строка, у нас число |
| 27 | MobilePhoneModel |
String |
object |
mobile_phone_model |
модель телефона; на десктопе пусто |
| 28 | ScreenWidth |
UInt16 |
uint16 |
screen_width |
ширина экрана в пикселях |
| 29 | ScreenHeight |
UInt16 |
uint16 |
screen_height |
высота экрана в пикселях |
| 30 | IPAddress |
String |
object |
ip_address |
IP-адрес посетителя |
| 31 | RegionCountry |
String |
object |
region_country |
страна кодом ISO |
| 32 | RegionCity |
String |
object |
region_city |
город, название по-английски |
| 33 | RegionCountryID |
UInt32 |
uint32 |
region_country_id |
числовой id страны в справочнике регионов Яндекса |
| 34 | RegionCityID |
UInt32 |
uint32 |
region_city_id |
числовой id города в том же справочнике |
Массивы и параметры
| № | Колонка | Тип ClickHouse | Тип numpy | Нормализованное имя | Комментарий |
|---|---|---|---|---|---|
| 35 | GoalsReached |
Array(UInt32) |
uint32 |
goals_reached |
id достигнутых целей; на стенде их две — корзина и покупка |
| 36 | ParsedParamsKey1 |
Array(String) |
object |
parsed_params_key1 |
свои параметры сайта, один уровень — например вариант A/B-теста |
Ecommerce
| № | Колонка | Тип ClickHouse | Тип numpy | Нормализованное имя | Комментарий |
|---|---|---|---|---|---|
| 37 | purchaseID |
Array(String) |
object |
purchase_id |
номер заказа; у события purchase — один элемент |
| 38 | purchaseRevenue |
Array(Float64) |
float64 |
purchase_revenue |
выручка заказа глазами клиента; Float64, как у Метрики — на этом держится урок о расхождениях с бэкендом |
| 39 | purchaseCurrency |
Array(String) |
object |
purchase_currency |
валюта заказа |
| 40 | purchaseCoupon |
Array(String) |
object |
purchase_coupon |
купон заказа, если был применён |
| 41 | productID |
Array(String) |
object |
product_id |
id товаров события |
| 42 | productName |
Array(String) |
object |
product_name |
названия тех же товаров |
| 43 | productCategory |
Array(String) |
object |
product_category |
категории тех же товаров |
| 44 | productPrice |
Array(Int64) |
int64 |
product_price |
цена за штуку целым числом: деньги генератор считает целыми |
| 45 | productQuantity |
Array(UInt64) |
uint64 |
product_quantity |
количество штук каждого товара |
| 46 | productEventType |
Array(String) |
object |
product_event_type |
действие с товаром: стенд шлёт add и purchase, полный словарь Метрики (detail, remove, impressions) не берём |
| 47 | ecommerce |
String |
object |
ecommerce |
сырой JSON события, как отдаёт Метрика — материал лабы про разбор JSON внутри колонки |