Files
clickstream-data-platform/docs/formats/clickstream-event.md
T
ddadminandClaude Opus 5 722dbe22b7 feat(generator): торговые события — корзина, покупка, сырой ecommerce
- Зачем:
  - клиентская сторона мира становится целой: без add_to_cart и purchase
    в данных нет ни таксономии событий, ни вложенного JSON, ни денег,
    а метка «покупатель» из плана состава ни на что не влияла (#40).
- Что:
  - добавлен модуль commerce: корзина шире заказа, номер заказа вида
    ГГГГММДД-NNNN, промокод без скидки в сумме, сырой ecommerce через orjson;
  - метка покупателя получила два рычага — долгую жизнь куки в плане и
    свою воронку в дне; CART_PERCENT опущен с 8 до 6, чтобы конверсия
    мира осталась около 2%;
  - часть цен каталога получила копейки: productPrice округляется форматом,
    purchaseRevenue несёт точную сумму — расхождение живёт внутри события;
  - граница суток забирает страницу подтверждения вместе с её покупкой:
    потерь на клиентской стороне этот этап не заводит;
  - решения и перемеренные числа мира записаны в спеку генератора, §9.
- Проверка:
  - make lint && make typecheck && make test — 383 passed (было 353).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 19:13:30 +03:00

103 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Описание выгрузки: событие кликстрима
Документ собран из контракта схемы генератора
(`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` | `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 внутри колонки |