(medium) houses.py/match_or_create_house Tier 0: cadastr_exact не регистрирует alias -> риск дубля при бескадастровом скрейпе #1537

Closed
opened 2026-06-15 05:59:29 +00:00 by bot-backend · 1 comment
Collaborator

Severity: medium · Категория: data-integrity · Файл: tradein-mvp/backend/app/services/matching/houses.py:79-90

houses.py:79-90. В ветке Tier 0 вызывается _upsert_house_source (:81) и return (:90), но НЕ _insert_alias — в отличие от Tier 2 (:151), Tier 3 (:184), New (:238). Поэтому fingerprint и normalized_address дома, впервые увиденного источником с кадастром (совпавшего с домом из БТИ), не попадают в house_address_aliases. Следующий скрейп того же дома из источника БЕЗ кадастра (типичный SERP) промахивается Tier 2a (fingerprint, :119) и Tier 2b (normalized_address, :127), и если гео-точка вне 30 м (дрожание провайдера) -> ветка New -> INSERT дубликата (:197). Подрывает дедуп ровно в сценарии, под который сделан Tier 2b (см. комментарий :111-117). Как чинить: добавить _insert_alias(db, house_id=house_id, address=address, fp=fp, source=ext_source) в ветку Tier 0 перед return на :90.

Почему баг: Если дом впервые увиден источником с кадастром (Tier 0 совпал с уже существующим домом из БТИ), его address-алиас не регистрируется. Следующий скрейп того же дома из источника БЕЗ кадастра (типичный SERP) промахивается Tier 2a и Tier 2b (алиаса нет), и если гео-точка вне 30 м (дрожание координат провайдера) — попадает в ветку 'New' и INSERT'ит дубликат дома. Это подрывает дедупликацию ровно в сценарии, ради которого Tier 2b и был добавлен (см. комментарий на строках 111-117).

Как чинить: Добавить _insert_alias(db, house_id=house_id, address=address, fp=fp, source=ext_source) в ветку Tier 0 перед return, как это сделано в Tier 2/3/New.


deep-audit «армия ботов 2» · verify+harden, confidence 0.88 · unit U130

**Severity:** medium · **Категория:** data-integrity · **Файл:** `tradein-mvp/backend/app/services/matching/houses.py:79-90` houses.py:79-90. В ветке Tier 0 вызывается _upsert_house_source (:81) и return (:90), но НЕ _insert_alias — в отличие от Tier 2 (:151), Tier 3 (:184), New (:238). Поэтому fingerprint и normalized_address дома, впервые увиденного источником с кадастром (совпавшего с домом из БТИ), не попадают в house_address_aliases. Следующий скрейп того же дома из источника БЕЗ кадастра (типичный SERP) промахивается Tier 2a (fingerprint, :119) и Tier 2b (normalized_address, :127), и если гео-точка вне 30 м (дрожание провайдера) -> ветка New -> INSERT дубликата (:197). Подрывает дедуп ровно в сценарии, под который сделан Tier 2b (см. комментарий :111-117). Как чинить: добавить _insert_alias(db, house_id=house_id, address=address, fp=fp, source=ext_source) в ветку Tier 0 перед return на :90. **Почему баг:** Если дом впервые увиден источником с кадастром (Tier 0 совпал с уже существующим домом из БТИ), его address-алиас не регистрируется. Следующий скрейп того же дома из источника БЕЗ кадастра (типичный SERP) промахивается Tier 2a и Tier 2b (алиаса нет), и если гео-точка вне 30 м (дрожание координат провайдера) — попадает в ветку 'New' и INSERT'ит дубликат дома. Это подрывает дедупликацию ровно в сценарии, ради которого Tier 2b и был добавлен (см. комментарий на строках 111-117). **Как чинить:** Добавить _insert_alias(db, house_id=house_id, address=address, fp=fp, source=ext_source) в ветку Tier 0 перед return, как это сделано в Tier 2/3/New. --- <sub>deep-audit «армия ботов 2» · verify+harden, confidence 0.88 · unit U130</sub>
bot-backend added the
week ревью 1
label 2026-06-15 05:59:29 +00:00
bot-backend added the
scope/backend
label 2026-06-15 11:02:20 +00:00
Owner

🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)

Текущее состояние кода: tradein-mvp/backend/app/services/matching/houses.py:69-90 (Tier 0, match_or_create_house). Ветка Tier 0: lookup houses WHERE cadastral_number = :cad (:71-78), при hit → _upsert_house_source(... method="cadastr_exact", confidence=1.0) (:81-88) → return (house_id, 1.0, "cadastr_exact") (:90). _insert_alias НЕ вызывается.

Корень: Tier 0 регистрирует только house_source link, но не alias. Контраст с другими ветками, которые вызывают _insert_alias(db, house_id=..., address=address, fp=fp, source=ext_source): Tier 2b (:151), Tier 3 (:184), New (:238). Поэтому fingerprint и normalized_address дома, впервые увиденного источником с кадастром (совпавшего с домом из БТИ), не попадают в house_address_aliases. Следующий скрейп того же дома из источника БЕЗ кадастра (типичный SERP) промахивается Tier 2a (fingerprint) и Tier 2b (normalized_address) из-за отсутствия алиаса, и если гео-точка вне 30 м (дрожание координат провайдера) → ветка New → INSERT дубликата дома. Это подрывает дедуп ровно в сценарии, ради которого Tier 2b и добавлен (комментарий :111-117).

Что править:

  • В ветку Tier 0 перед return (:90) добавить _insert_alias(db, house_id=house_id, address=address, fp=fp, source=ext_source) — как в Tier 2/3/New. Проверить, что fp/address/ext_source уже доступны в скоупе на момент Tier 0 (alias-вставка идемпотентна — _insert_alias docstring :370 описывает UPSERT с обновлением fingerprint).

Что посмотреть:

  • Код: matching/houses.py:69-90 (Tier 0), :151 (Tier 2b alias), :184 (Tier 3 alias), :238 (New alias), :357- (_insert_alias сигнатура/UPSERT), :111-117 (комментарий-обоснование Tier 2b). NB: read-only вариант match_house (:269-284) тоже имеет Tier 0 без alias, но он read-only — алиасы не пишет by design, его трогать не надо. Тест: scrape с кадастром (Tier 0 hit) → потом scrape без кадастра, координаты сдвинуты >30м → должен матчиться по alias, не создавать дубль.
  • Vault: old/decisions/Cross_Source_Matching_Strategy.md (описывает 3-tier house matching + таблицу house_address_aliases; архивная, но релевантна по дизайну дедупа).
**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** **Текущее состояние кода:** `tradein-mvp/backend/app/services/matching/houses.py:69-90` (Tier 0, `match_or_create_house`). Ветка Tier 0: lookup `houses WHERE cadastral_number = :cad` (:71-78), при hit → `_upsert_house_source(... method="cadastr_exact", confidence=1.0)` (:81-88) → `return (house_id, 1.0, "cadastr_exact")` (:90). `_insert_alias` НЕ вызывается. **Корень:** Tier 0 регистрирует только house_source link, но не alias. Контраст с другими ветками, которые вызывают `_insert_alias(db, house_id=..., address=address, fp=fp, source=ext_source)`: Tier 2b (:151), Tier 3 (:184), New (:238). Поэтому fingerprint и normalized_address дома, впервые увиденного источником с кадастром (совпавшего с домом из БТИ), не попадают в `house_address_aliases`. Следующий скрейп того же дома из источника БЕЗ кадастра (типичный SERP) промахивается Tier 2a (fingerprint) и Tier 2b (normalized_address) из-за отсутствия алиаса, и если гео-точка вне 30 м (дрожание координат провайдера) → ветка New → INSERT дубликата дома. Это подрывает дедуп ровно в сценарии, ради которого Tier 2b и добавлен (комментарий :111-117). **Что править:** - В ветку Tier 0 перед `return` (:90) добавить `_insert_alias(db, house_id=house_id, address=address, fp=fp, source=ext_source)` — как в Tier 2/3/New. Проверить, что `fp`/`address`/`ext_source` уже доступны в скоупе на момент Tier 0 (alias-вставка идемпотентна — `_insert_alias` docstring :370 описывает UPSERT с обновлением fingerprint). **Что посмотреть:** - Код: `matching/houses.py:69-90` (Tier 0), `:151` (Tier 2b alias), `:184` (Tier 3 alias), `:238` (New alias), `:357-` (`_insert_alias` сигнатура/UPSERT), `:111-117` (комментарий-обоснование Tier 2b). NB: read-only вариант `match_house` (:269-284) тоже имеет Tier 0 без alias, но он read-only — алиасы не пишет by design, его трогать не надо. Тест: scrape с кадастром (Tier 0 hit) → потом scrape без кадастра, координаты сдвинуты >30м → должен матчиться по alias, не создавать дубль. - Vault: `old/decisions/Cross_Source_Matching_Strategy.md` (описывает 3-tier house matching + таблицу `house_address_aliases`; архивная, но релевантна по дизайну дедупа).
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#1537
No description provided.