gendesign/tradein-mvp/backend/data/sql/070_houses_dadata_enrichment.sql
Light1YT e332052d0e feat(tradein): DaData /clean/address backfill для houses (PR Q3)
Migration 070 — добавляет houses.house_fias_id + dadata_qc_geo / qc_house /
enriched_at для tracking. Скрипт scripts/backfill_houses_dadata.py
проходит по ~3.8k rows с NULL coords + ~8.6k с NULL cadnum daily 100/day
(DaData demo quota → ~124 дня walltime для полного прохода).

Per-row SAVEPOINT, resume-safe (фильтр dadata_enriched_at IS NULL),
qc_geo IN (0,1) → overwrite coord-payload, иначе только tracking.
--priority coords|cadnum|both, --dry-run, --limit N (default 100).

20 тестов покрывают happy path qc_geo=0, qc_geo=3 city-level skip,
DaData None response, per-row exception isolation, --dry-run, --limit,
--priority filter, и missing-creds fail-fast.
2026-05-27 19:43:07 +05:00

62 lines
3.5 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 070_houses_dadata_enrichment.sql
-- PR Q3: DaData backfill полей на таблице `houses`.
--
-- После PR Q1 (#604) сервис `dadata.clean_address()` живёт на проде и обогащает
-- адреса estimator-flow on-demand. Этот скрипт (`scripts/backfill_houses_dadata.py`)
-- проходит по `houses` daily 100/day (DaData demo quota) и заполняет:
-- - lat / lon (3801 rows без координат)
-- - cadastral_number (12437 rows — 100% NULL)
-- - house_fias_id — НОВАЯ колонка, симметрия с trade_in_estimates.house_fias_id (PR Q1)
--
-- Колонки dadata_qc_geo / dadata_qc_house / dadata_enriched_at — tracking:
-- помечают что попытка enrichment была сделана, даже если результат не записан
-- (quality codes 2/3/4 — слишком низкое разрешение, не overwrite coords).
-- Это позволяет:
-- 1) resume-safe backfill: `WHERE dadata_enriched_at IS NULL` пропускает обработанные
-- 2) re-enrich stale: при необходимости фильтр `dadata_enriched_at < NOW() - 30 days`
--
-- Все поля nullable + IF NOT EXISTS → миграция идемпотентна.
--
-- Dependencies: 009_houses.sql + 010_houses_alter.sql + 029_extend_matching_valuation_dynamics.sql
-- Apply after: 069_trade_in_estimates_dadata_fields.sql
BEGIN;
ALTER TABLE houses
ADD COLUMN IF NOT EXISTS house_fias_id text,
ADD COLUMN IF NOT EXISTS dadata_qc_geo integer,
ADD COLUMN IF NOT EXISTS dadata_qc_house integer,
ADD COLUMN IF NOT EXISTS dadata_enriched_at timestamptz;
-- Индекс на ФИАС для будущих JOIN'ов между houses ↔ trade_in_estimates (PR Q1).
CREATE INDEX IF NOT EXISTS houses_fias_id_idx
ON houses (house_fias_id)
WHERE house_fias_id IS NOT NULL;
-- Cadastral_number индекс уже создан в 029_extend_matching_valuation_dynamics.sql
-- (имя houses_cadastral_number_idx) и в 050_search_optimization.sql (houses_kadastr_idx2).
-- IF NOT EXISTS → no-op при повторном создании. Оставляем для idempotent docs.
CREATE INDEX IF NOT EXISTS houses_cadastral_number_idx
ON houses (cadastral_number)
WHERE cadastral_number IS NOT NULL;
-- Partial index для быстрого выбора кандидатов backfill'а:
-- `WHERE dadata_enriched_at IS NULL ORDER BY id`. После полного прохода
-- (12437 row → ~124 дня walltime) индекс схлопнется до пустого set.
CREATE INDEX IF NOT EXISTS houses_dadata_not_enriched_idx
ON houses (id)
WHERE dadata_enriched_at IS NULL;
COMMENT ON COLUMN houses.house_fias_id IS
'DaData UUID ФИАС дома (PR Q3, #604+#605 follow-up). '
'Заполняется backfill-скриптом scripts/backfill_houses_dadata.py.';
COMMENT ON COLUMN houses.dadata_qc_geo IS
'DaData qc_geo: 0=exact, 1=street, 2=settlement, 3=city. '
'Только qc_geo IN (0,1) триггерит overwrite lat/lon в backfill.';
COMMENT ON COLUMN houses.dadata_qc_house IS
'DaData qc_house: 2=в ФИАС, 10=на картах, иначе не найден.';
COMMENT ON COLUMN houses.dadata_enriched_at IS
'Когда последний раз пробовали enrich через DaData (NULL = ни разу). '
'Используется для resume-safe backfill — `WHERE dadata_enriched_at IS NULL`.';
COMMIT;