fix(tradein/matching): не сваливать листинги в чужой дом (P1-P3 хардненинг матчера) #2082

Merged
lekss361 merged 1 commit from fix/tradein-house-match-hardening into main 2026-06-28 21:07:39 +00:00
Owner

Корень (по факту прода)

listings.house_id_fkmatch_or_create_house (3-тира: cadastral → fingerprint/normalized_address → гео-30м → new). Дом 131237 «Мамина-Сибиряка 126» стянул в один house 8 разных номеров (Мамина 2/8/36б/52/64/126/130/132) + голую улицу без номера → числитель доли загрязнён чужими листингами.

Два механизма:

  1. Голый street-alias (normalized_address без номера) — любой безномерный листинг (Яндекс-SERP) матчится в него Tier-2b → масс-сток.
  2. Гео-30м на грубых street-centroid геокодах — разные номера (52/64/126) попадают в 30м одной точки → матч; и матч цементируется новым alias, ошибка нарастает.

Гарды (только НОВЫЕ матчи; Tier0/Tier1 не трогаем)

  • P1 — не матчить/алиасить голую улицу: Tier-2b (normalized_address) под and has_num; _insert_alias — no-op без номера дома.
  • P2address-consistency на Tier-3 гео: гео-кандидат отклоняется, если у листинга и дома оба есть номер и они различаются (падаем в New-house). Принимаем только при совпадении токенов или отсутствии номера с одной стороны. Тот же гард в match_house_readonly (гео-50м).
  • P3не алиасить слабый гео-матч (0.7) — снимаем _insert_alias в ветке geo_proximity (останавливает снежный ком). Алиасы по-прежнему пишутся для cadastr/fingerprint/new.

Хелперы has_house_number / house_number_token — по хвостовому токену ((?:^|\s)\d\S*$), чтобы «улица 8 марта» (цифра в названии) НЕ считалась номером дома, а «улица 8 марта 5» → токен 5.

Проверки

  • ruff clean; pytest -k "match or normalize or house"603 passed, 0 failed. Юнит (хелперы) + поведенческие (P1 не алиасит голую улицу, P2 reject на разных номерах → New, P3 гео не алиасит).
  • Аффектит только НОВЫЕ first-time матчи (Tier1 source_exact короткозамыкает раньше) — существующие cemented-данные правит отдельная P4-миграция (чистка голых/мульти-номерных алиасов + ре-backfill house_id_fk).

Caveat

Если short_address дома без номера (редкий регион-only кейс) → cand_token=None → гео принимается (fallback к прежнему поведению, не регресс).

## Корень (по факту прода) `listings.house_id_fk` ← `match_or_create_house` (3-тира: cadastral → fingerprint/normalized_address → гео-30м → new). Дом 131237 «Мамина-Сибиряка 126» стянул в один house **8 разных номеров** (Мамина 2/8/36б/52/64/126/130/132) **+ голую улицу без номера** → числитель доли загрязнён чужими листингами. Два механизма: 1. **Голый street-alias** (normalized_address без номера) — любой безномерный листинг (Яндекс-SERP) матчится в него Tier-2b → масс-сток. 2. **Гео-30м на грубых street-centroid геокодах** — разные номера (52/64/126) попадают в 30м одной точки → матч; и матч **цементируется новым alias**, ошибка нарастает. ## Гарды (только НОВЫЕ матчи; Tier0/Tier1 не трогаем) - **P1** — не матчить/алиасить **голую улицу**: Tier-2b (`normalized_address`) под `and has_num`; `_insert_alias` — no-op без номера дома. - **P2** — **address-consistency на Tier-3 гео**: гео-кандидат отклоняется, если у листинга и дома оба есть номер и они **различаются** (падаем в New-house). Принимаем только при совпадении токенов или отсутствии номера с одной стороны. Тот же гард в `match_house_readonly` (гео-50м). - **P3** — **не алиасить слабый гео-матч (0.7)** — снимаем `_insert_alias` в ветке `geo_proximity` (останавливает снежный ком). Алиасы по-прежнему пишутся для cadastr/fingerprint/new. Хелперы `has_house_number` / `house_number_token` — по **хвостовому токену** (`(?:^|\s)\d\S*$`), чтобы «улица 8 марта» (цифра в названии) НЕ считалась номером дома, а «улица 8 марта 5» → токен `5`. ## Проверки - ruff clean; `pytest -k "match or normalize or house"` → **603 passed**, 0 failed. Юнит (хелперы) + поведенческие (P1 не алиасит голую улицу, P2 reject на разных номерах → New, P3 гео не алиасит). - Аффектит **только НОВЫЕ first-time матчи** (Tier1 source_exact короткозамыкает раньше) — существующие cemented-данные правит **отдельная P4-миграция** (чистка голых/мульти-номерных алиасов + ре-backfill house_id_fk). ## Caveat Если `short_address` дома без номера (редкий регион-only кейс) → `cand_token=None` → гео принимается (fallback к прежнему поведению, не регресс).
lekss361 added 1 commit 2026-06-28 21:06:47 +00:00
fix(tradein/matching): не сваливать листинги в чужой дом (P1-P3)
All checks were successful
CI / changes (pull_request) Successful in 8s
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
384c4e7ac3
Три гарда в match_or_create_house (+ P2 в match_house_readonly):
- P1: не матчить/алиасить голую улицу (normalized_address без номера дома)
- P2: отклонять Tier-3 гео-кандидата, если номера домов различаются
- P3: не цементировать слабый гео-матч (0.7) как alias-ключ здания

Хелперы has_house_number/house_number_token (по хвостовому токену —
«улица 8 марта» это улица, не дом). Корень mis-bucketing: house 131237
стянул 8 разных Мамина-номеров + голую улицу. Юнит + поведенческие тесты,
603 matching/normalize/house зелёных. Аффектит только НОВЫЕ матчи;
чистка cemented-данных — отдельной миграцией (P4).
lekss361 merged commit ec4c7e9e6a into main 2026-06-28 21:07:39 +00:00
lekss361 deleted branch fix/tradein-house-match-hardening 2026-06-28 21:07:39 +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#2082
No description provided.