Локальный индекс считает медианы по своему региону, а статус точек интереса не врёт #3517

Merged
lekss361 merged 2 commits from fix/msk-location-index-region into main 2026-09-13 11:14:45 +00:00

2 commits

Author SHA1 Message Date
bot-backend
3fae2b4ed8 fix(tradein): убрать OR region_code IS NULL из median-SQL location_index
All checks were successful
CI Trade-In / changes (pull_request) Successful in 16s
CI Trade-In / browser-tests (pull_request) Has been skipped
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 / changes (pull_request) Successful in 19s
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m33s
Строгое region_code = CAST(:region_code AS integer), без OR-ветки на NULL.

Второй прод-замер (13.09.2026, полный набор фильтров модуля — is_active,
price sanity, geo_precision, свежесть 14 дней, сегмент, bbox ядра 66):
15 858 строк проходят фильтры, из них 3 с NULL region_code. Медиана с ними
151 101 ₽/м², без них 151 103 — сдвиг 2 рубля на 151 тысяче (0.001%),
для региона 66 статистически ничто.

Мягкий OR NULL был бы лазейкой того же класса, что и сам дефект: в
listings 16 930 NULL-region строк Avito, 24 из них уже лежат в bbox
Москвы (сегодня не проходят по свежести, но могут завтра) — OR NULL
тихо открыл бы им дорогу обратно в московскую медиану тем же путём,
каким туда раньше попадала Московская область.

Тесты: test_median_queries_do_not_admit_null_region_code (обе median-SQL
не содержат "OR region_code" / "region_code IS NULL"), комментарий перед
SQL переписан под новое решение с этими числами.
2026-09-13 13:45:32 +03:00
bot-backend
def21dc796 fix(tradein): location_index не смешивает регионы и не врёт про POI для Москвы
Дефект 1: bbox продукт-ядра Москвы (55.55-55.95/37.30-37.90) — прямоугольник,
не административная граница, и захватывает соседние Химки/Реутов/Котельники/
Люберцы (region_code=50). Обе SQL-медианы (_MEDIAN_PPM2_LOCAL_SQL/CITYWIDE_SQL)
фильтровали листинги только по bbox, без region_code — прод-замер 13.09.2026:
городская медиана Москвы получалась заниженной на 7.9% (422 222 вместо 458 208
₽/м²) из-за примеси 16 801 подмосковного лота. У областных строк city IS NULL,
поэтому фильтровать по имени города нельзя — добавлен bind-параметр
region_code, заполняемый из уже определённого для точки Region.code.
Для 66 сохранено byte-for-byte: `OR region_code IS NULL` оставляет в пуле
некоторое количество legacy-листингов без региональной метки — ровно то же
множество строк, что и старый запрос без фильтра вовсе (проверено на проде:
внутри bbox 66 других region_code, кроме 66 и NULL, не существует).

Дефект 2: гейт _fetch_nearby_poi решал poi_status по count(*) ПО ВСЕЙ
osm_poi_ekb_local (4 850 екатеринбургских строк, всегда > 0) — для Москвы
это давало poi_status="ok" с пустым списком «что рядом», хотя внутри
московского продукт-ядра POI нет вообще. Гейт теперь считает count в
bbox_product_core региона точки; при 0 — тот же статус "unavailable",
что и раньше (терминология не расширяется, trade_in.py трактует его как
«нет данных» без изменений).

Тесты: tests/services/test_location_index.py — добавлены проверки bind-
параметра region_code в обеих median-SQL, региональной изоляции gate'а POI
(bbox запроса совпадает с bbox_product_core переданного региона), резолва
московской точки в region 77, и end-to-end сценария для Москвы (region_code
долетает в запрос, poi_status="unavailable"). 43/43 pytest зелёные,
ruff check + ruff format --check чисто.
2026-09-13 13:30:54 +03:00