Files
clickstream-data-platform/docs/labs/top-pages-daily.md
T
ddadmin cffe1aeb73 docs(labs): обращение «вы» во всех учебных заданиях
- Зачем:
  - README и путеводитель обращаются к читателю на «вы», а ТЗ в docs/labs/
    были на «ты»; после пересборки индекса смесь стала заметна.
- Что:
  - во всех восьми файлах docs/labs/ местоимения, глаголы и повелительное
    наклонение переведены во второе лицо множественного числа.
  - названия ступеней в таблице README оставлены как в спеке.
- Проверка:
  - grep по формам «ты/твой», 'ё' и 'менти' по docs/labs/ молчит;
    git diff --check чистый; ревью диффа свежей сессией.
2026-09-03 17:29:13 -04:00

115 lines
8.3 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.
# Задание 2: `dm.top_pages_daily`
**Чему учит:** зерно сменилось — значит витрина материализуется, живет
дневными партициями и встает в конвейер. Первая настоящая витрина: пять
объектов, донор, замена партиции, задача в `dm_transform`.
## Роль витрины
Популярность страниц по дням: сколько раз страницу открыли, сколько разных
посетителей ее видели и что на ней делали кроме просмотра.
Торговое событие садится на ту страницу, где случилось: корзина — на
карточку товара, покупка — на страницу подтверждения заказа. Поэтому у
карточки товара рядом стоят просмотры и корзины, и их отношение —
конверсия карточки — главный вопрос к этой витрине: какие товары кладут в
корзину, едва открыв, а какие смотрят и уходят.
Воронки «просмотр → корзина → покупка» здесь нет и быть не может: корзина и
покупка живут на разных страницах, и связать их шаги в одну цепочку на
зерне страницы не из чего. Путь одного человека собирают из событий или из
сессий, а витрина страниц отвечает про страницу.
## Зерно и форма
Зерно — **день события × страница**. Источник — события, у него зерно
события, значит зерно сменилось и витрина материализуется:
`dm.top_pages_daily_rep` / `_dist`, донор `_stage_rep` / `_stage_dist`,
публичное `dm.top_pages_daily_v`.
Страница — это `page_path`, путь `url` без домена и параметров запроса;
считается он здесь же, из колонки `url` источника. Полный адрес в зерно не
берется: с параметрами запроса одна и та же страница рассыпалась бы на
сотни строк, и витрина перестала бы отвечать на свой вопрос.
Ключ партиции — день: он же единица пересборки.
Источник — `dds.event_v`, публичный договор события. Витрина задания 1
источником не служит: витрина слоя DM читает договор DDS, как и стендовые
витрины, и не зависит от того, выполнена ли соседняя домашка.
## Состав колонок
| Колонка | Тип | Что это |
|---|---|---|
| `report_date` | `Date` | день события |
| `page_path` | `LowCardinality(String)` | путь страницы |
| `pageviews` | `UInt64` | число событий `pageview` на странице |
| `visitors` | `UInt64` | число разных `client_id` на странице |
| `cart_adds` | `UInt64` | число событий `add_to_cart` на странице |
| `purchases` | `UInt64` | число событий `purchase` на странице |
| `_load_id` | `String` | запуск, собравший день |
| `_load_ts` | `DateTime64(3, 'UTC')` | когда собран |
Счетчики условные: строку дают все три типа событий, а различает их
`countIf`. Отфильтровать источник по `event_type = 'pageview'`, как
подсказывает имя витрины, — значит получить витрину из одних просмотров:
страницы останутся на месте, а `cart_adds` и `purchases` у всех строк
обнулятся. Ошибка тихая: витрина выглядит собранной, и главный ее вопрос —
конверсия карточки — молча отвечает «ноль везде».
## Ритм пересборки
Источник — события: день замерзает сразу, как только прожит. Значит
пересобирать нужно только то, что еще не собрано: хвост дней, которых в
витрине нет, — от первого несобранного прожитого дня до последнего
прожитого. Готовые дни трогать незачем, а вот дыру, оставленную частичным
отказом, объем прогона обязан подобрать: ищите не последний собранный день, а
первый несобранный.
Механика — сборка отрезка в донор одной вставкой и замена дневных партиций
по одной, как у `revenue_daily`.
## Ключ шардирования
Ваше решение по [правилу донора](../architecture/storage.md), «Раскладка по
шардам». Подумайте, чем эта витрина отличается от `revenue_daily`: там кроме
дня было одно измерение из шести близких по объему значений, здесь —
страницы, которых много и которые по популярности очень неровные.
## Как проверить себя
Деловой ключ — `page_path` внутри дня. Соберите прямой агрегат из
`dds.event_v` за день, соедините с `dm.top_pages_daily_v` полным внешним
соединением по ключу и посчитайте строки, где ключа нет с одной из сторон или
где расходится любой из четырех счетчиков. Ноль — зеленый.
Две проверки попроще, ими удобно ловить грубую ошибку до полного
соединения:
- сумма `pageviews` за день равна числу событий `pageview` этого дня в
`dds.event_v`;
- сумма `purchases` за день равна числу событий `purchase` этого дня.
Проверки ловят разное. Фильтр `WHERE event_type = 'pageview'` первая из них
пропускает: отбор строк по типу не меняет счет событий того же типа.
Расхождение покажет только вторая — вместе с `cart_adds` обнулятся и
`purchases`. Проверку выбирают по тому, умеет ли она покраснеть на вашей
ошибке, а не по тому, как быстро она пишется: [проверка, которая не умеет
краснеть, бесполезна](../architecture/testing.md).
`visitors` складывать по строкам нельзя: один посетитель ходит по разным
страницам и в сумме посчитается несколько раз. Число посетителей дня
спрашивают у `dm.daily_traffic_v`, а не у этой витрины.
## Приемка
- [ ] пять объектов витрины созданы, донор объявлен через `AS` и шардируется
ключом цели;
- [ ] `dm.top_pages_daily_v` перечисляет колонки явно, включая `_load_id` и
`_load_ts`;
- [ ] объем прогона выводится из состояния витрины, а не задается руками;
- [ ] задача `top_pages_daily` в `homework_check` зеленая;
- [ ] задача витрины добавлена в `dm_transform`, повторный прогон конвейера
результат не меняет.