fix(tradein/geocoder): строгий матч литеры дома + починка DaData region-фильтра #2622
Merged
lekss361
merged 2 commits from 2026-08-02 12:06:45 +00:00
fix/tradein-geocoder-house-letter into main
2 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9f03733e46 |
docs(tradein/geocoder): исправить докстринг — в geocode() нет тира DaData
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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
CI Trade-In / backend-tests (pull_request) Successful in 2m48s
Докстринг _cadastral_house_match утверждал, что промах уходит в цепочку _cadastral_forward_sync → DaData → Nominatim. Тира DaData в geocode() нет: _dadata_suggest вызывается ровно в одном месте — внутри suggest() (автокомплит). Реальные цепочки разведены явно, плюс зафиксировано следствие: на прямом вызове geocode() адрес с литерой, неизвестный геопорталу и Nominatim, даёт None вместо оценки соседнего дома. Основной UI-путь не задет — координаты приходят из выбранной подсказки и минуют geocode(). |
||
|
|
d63702cfec |
fix(tradein/geocoder): литера дома — часть идентичности, а не tie-break
All checks were successful
CI / changes (pull_request) Successful in 8s
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
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
CI Trade-In / backend-tests (pull_request) Successful in 2m44s
`_cadastral_house_match` сравнивал дом только по ЦИФРАМ: литера была
опциональна в WHERE (`[а-яё]?`) и участвовала лишь как tie-break в
ORDER BY. Баг двусторонний и молчаливый:
«Новгородцевой 13б» → «дом 13» (дома 13б в реестре нет вообще)
«Малышева 30» → «д. 30-б» (обратное направление, живой прод-кейс)
Оба результата возвращались с `confidence="exact"` и оседали в
`geocode_cache` на 90 дней — пользователь получал оценку ЧУЖОГО здания,
помеченную как точная.
Почему так: номер дома теперь сравнивается РАВЕНСТВОМ нормализованных
форм. Обе стороны приводятся к одному канону (`13б` / `13 б` / `13-б` /
`13Б` → `13б`): запрос — существующим `_norm_house`, реестр — тем же
выражением на стороне Postgres. Дешёвый regex-anchor по цифрам оставлен
prefilter'ом (пушится в FDW, режет «(1-83)»-диапазоны), но корректность
теперь на равенстве, а не на нём.
Побочно закрыты той же логикой:
• «58» больше не матчит «58/3», «13» не матчит «130» — угловой номер
это часть номера дома, а не мусор;
• маркер дома ищется только с начала слова (`\m`), иначе «проезд 8
Марта, д 5» давал дом «8» — старый `д\.?` ловил «д» внутри «проезд»
(20 таких строк реестра были достижимы ТОЛЬКО через этот баг);
• номер дома больше не конкатенируется в regex — regex-injection
поверхность сузилась до цифр prefilter'а.
Промаха не превращаем в тихую подстановку соседа: нет дома с нужной
литерой → None, дальше отрабатывают следующие тиры (forward → DaData →
Nominatim). Понижать confidence здесь нечем — `GeocodeSuggestion` его не
несёт, все локальные тиры хардкодят "exact"; честный фолбэк дешевле, чем
протаскивать новый канал уверенности через три тира.
DaData-тир, который должен подхватывать такие адреса, был мёртв: в
`locations` уходило `region="Свердловская область"`, тогда как DaData
хранит имя региона БЕЗ типа (`region="Свердловская"`, `region_type="обл"`).
Hard-filter не совпадал ни с чем и молча схлопывал выдачу в 0 подсказок
(замер на проде: 0 хитов против 5 с «Свердловская», первый — искомый
«д 13б» с fias_id). Добавлен warning на пустую выдачу под region-
констрейнтом, чтобы следующая такая регрессия не была невидимой.
Миграция 203 инвалидирует уже отравленные записи `geocode_cache` —
точечно (запрос с литерой, либо запрос без литеры с закэшированным
домом С литерой), а не весь кэш: полная очистка сожгла бы квоту DaData
на ре-резолв заведомо корректных адресов.
Проверено: SQL-выражение прогнано против живого реестра (47k строк) —
«13б»→∅, «13»→дом 13, «30»→сооружение 30 (не «30-б»), «23б»→«д. 23-б»;
предикат миграции — против таблицы кейсов (4 delete / 8 keep).
Полный pytest: 1 failed, 3123 passed — падение
`test_search_api.py::test_search_cache_hit` (401) воспроизводится и на
чистом main (baseline 1 failed, 3081 passed), к этому изменению
отношения не имеет.
НЕ проверено вживую: ответ DaData с новым region-констрейнтом (нет
доступа к прод-токену из этой сессии) — опираюсь на замер из отчёта об
issue и на модель данных DaData.
|