feat(tradein): канонический матч ГАР↔houses (0%→41% yield) + tradein_canon_addr + --match-only #2063

Merged
lekss361 merged 1 commit from feat/tradein-gar-canon-match into main 2026-06-28 15:50:08 +00:00
Owner

Summary

ГАР-данные загружены (919 341 дом региона 66, 68 406 с числом квартир), но точный матч tradein_normalize_short_addr дал 0 совпадений — функция почти не канонизирует, и houses."ул. Шаумяна,20" никогда не равно ГАР "ул. Шаумяна, 20".

Замеряно на живых данных: канонический ключ поднимает yield до 2 769 из 6 808 вторичных домов (40.7%), пары корректны (ул. Шаумяна,20шаумяна20 = ГАР ул. Шаумяна, 20; р-н Ленинский, улица Цвиллинга, 7/6цвиллинга76).

  • 144_gar_canon_addr_match.sqltradein_canon_addr(text) IMMUTABLE (lower, ё→е, drop р-н …,, strip типы улиц, keep [а-я0-9]) + partial functional index. Идемпотентно.
  • gar_flats_loader.match_houses_to_gar — канон-join, ЕКБ-фильтр (full_address ILIKE :city, default «Екатеринбург» — избегаем коллизий улиц между городами региона, напр. Машиностроителей 6 в 4 городах), DISTINCT ON (canon) … flat_count DESC (детерминированный tie-break = max квартир), idempotent IS DISTINCT FROM, gar_match_method='canon_addr'. Параметры city/region — bind (CAST(:x AS text)), не интерполяция.
  • --match-only в CLI — прогнать ТОЛЬКО матч без повторного парса многогигабайтных XML (после деплоя так и запущу).
  • Корректно НЕ схлопывает 6 (литера = другой дом).

Качество

  • code-reviewer: APPROVE (SQL-injection safe, IMMUTABLE для индекса, детерминир./идемпотентный матчер, миграция 144 без коллизий).
  • Тесты: test_gar_flats_loader.py 22 passed; полный сьют 2660 passed, 1 fail (test_search_cache_hit — pre-existing, auth, не связан).
  • Yield пере-подтверждён на проде (ROLLBACK-tx): 2769/6808 = 40.7%.

Test plan

  • Канон-yield на проде (rollback) = 40.7%; пары корректны.
  • После деплоя (миграция 144 + новый матчер): docker exec tradein-backend python -m app.tasks.gar_flats_load --match-only --region 66houses.gar_flat_count заполнен ~2769 домам → страница показывает реальный %.

Остаток до live-%: этот PR + прогон --match-only. ~59% домов без знаменателя (нет в ГАР квартир / литера/корпус-расхождения) — честно показываем «н/д»; city-aware матч за пределами ЕКБ + trgm — future work.

## Summary ГАР-данные загружены (919 341 дом региона 66, 68 406 с числом квартир), но **точный** матч `tradein_normalize_short_addr` дал **0 совпадений** — функция почти не канонизирует, и `houses."ул. Шаумяна,20"` никогда не равно ГАР `"ул. Шаумяна, 20"`. Замеряно на живых данных: **канонический ключ** поднимает yield до **2 769 из 6 808** вторичных домов (**40.7%**), пары корректны (`ул. Шаумяна,20`→`шаумяна20` = ГАР `ул. Шаумяна, 20`; `р-н Ленинский, улица Цвиллинга, 7/6`→`цвиллинга76`). - **`144_gar_canon_addr_match.sql`** — `tradein_canon_addr(text)` IMMUTABLE (lower, ё→е, drop `р-н …,`, strip типы улиц, keep `[а-я0-9]`) + partial functional index. Идемпотентно. - **`gar_flats_loader.match_houses_to_gar`** — канон-join, ЕКБ-фильтр (`full_address ILIKE :city`, default «Екатеринбург» — избегаем коллизий улиц между городами региона, напр. `Машиностроителей 6` в 4 городах), `DISTINCT ON (canon) … flat_count DESC` (детерминированный tie-break = max квартир), idempotent `IS DISTINCT FROM`, `gar_match_method='canon_addr'`. Параметры city/region — bind (`CAST(:x AS text)`), не интерполяция. - **`--match-only`** в CLI — прогнать ТОЛЬКО матч без повторного парса многогигабайтных XML (после деплоя так и запущу). - Корректно НЕ схлопывает `6Б` ≠ `6` (литера = другой дом). ## Качество - code-reviewer: ✅ APPROVE (SQL-injection safe, IMMUTABLE для индекса, детерминир./идемпотентный матчер, миграция 144 без коллизий). - Тесты: `test_gar_flats_loader.py` 22 passed; полный сьют 2660 passed, 1 fail (`test_search_cache_hit` — pre-existing, auth, не связан). - Yield пере-подтверждён на проде (ROLLBACK-tx): 2769/6808 = 40.7%. ## Test plan - [x] Канон-yield на проде (rollback) = 40.7%; пары корректны. - [ ] После деплоя (миграция 144 + новый матчер): `docker exec tradein-backend python -m app.tasks.gar_flats_load --match-only --region 66` → `houses.gar_flat_count` заполнен ~2769 домам → страница показывает реальный %. Остаток до live-%: этот PR + прогон `--match-only`. ~59% домов без знаменателя (нет в ГАР квартир / литера/корпус-расхождения) — честно показываем «н/д»; city-aware матч за пределами ЕКБ + trgm — future work.
lekss361 added 1 commit 2026-06-28 15:49:46 +00:00
feat(tradein): канонический матч ГАР↔houses (0%→41% yield) + tradein_canon_addr + --match-only
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
c729a39446
lekss361 merged commit 579536b4ad into main 2026-06-28 15:50:08 +00:00
lekss361 deleted branch feat/tradein-gar-canon-match 2026-06-28 15:50:08 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2063
No description provided.