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

8.3 KiB

Задание 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.

Ключ шардирования

Ваше решение по правилу донора, «Раскладка по шардам». Подумайте, чем эта витрина отличается от 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. Проверку выбирают по тому, умеет ли она покраснеть на вашей ошибке, а не по тому, как быстро она пишется: проверка, которая не умеет краснеть, бесполезна.

visitors складывать по строкам нельзя: один посетитель ходит по разным страницам и в сумме посчитается несколько раз. Число посетителей дня спрашивают у dm.daily_traffic_v, а не у этой витрины.

Приемка

  • пять объектов витрины созданы, донор объявлен через AS и шардируется ключом цели;
  • dm.top_pages_daily_v перечисляет колонки явно, включая _load_id и _load_ts;
  • объем прогона выводится из состояния витрины, а не задается руками;
  • задача top_pages_daily в homework_check зеленая;
  • задача витрины добавлена в dm_transform, повторный прогон конвейера результат не меняет.