-- 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;