diff --git a/data/sql/117_obj2_mapping_backfill_fast.sql b/data/sql/117_obj2_mapping_backfill_fast.sql new file mode 100644 index 00000000..b6afeea3 --- /dev/null +++ b/data/sql/117_obj2_mapping_backfill_fast.sql @@ -0,0 +1,91 @@ +-- 117_obj2_mapping_backfill_fast.sql +-- Issue #307 OBJ-2 (continued from 116 hotfix). +-- +-- Migration 116 был trimmed до GIST index only из-за heavy CTE timeout. +-- Эта migration делает фактический INSERT через **pre-materialized temp tables** +-- (avoid full CROSS JOIN LATERAL scan). Composite score = name 0.6 + dev 0.25 + district 0.15. +-- Threshold composite >= 0.75. ON CONFLICT DO NOTHING (idempotent). +-- +-- Estimated runtime после оптимизации: <30s (vs old 89M-cost timeout). +-- Coverage цель: +50-80 mappings → объединённый total ~200. + +BEGIN; + +-- Pre-materialize unmapped DOM.РФ ЕКБ objects (~1056 rows) +CREATE TEMP TABLE _u_domrf ON COMMIT DROP AS +SELECT o.obj_id, o.comm_name, o.dev_name, o.district_name, + regexp_replace(lower(coalesce(o.dev_name, '')), '[^а-яa-z0-9]', '', 'g') AS dev_norm + FROM domrf_kn_objects o + WHERE o.is_ekb = true + AND o.comm_name IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM objective_complex_mapping m WHERE m.domrf_obj_id = o.obj_id + ); + +-- Pre-materialize distinct unmapped Objective projects (~178 rows) +CREATE TEMP TABLE _u_obj ON COMMIT DROP AS +SELECT DISTINCT + l.project_name, + l.developer, + l.district, + regexp_replace(lower(coalesce(l.developer, '')), '[^а-яa-z0-9]', '', 'g') AS dev_norm + FROM objective_lots l + WHERE l.project_name IS NOT NULL + AND l.project_name NOT IN ( + SELECT objective_complex_name FROM objective_complex_mapping + ); + +-- Top-3 candidates per DOM.РФ obj_id using GIST trgm index (from migration 116) +CREATE TEMP TABLE _pairs ON COMMIT DROP AS +SELECT + d.obj_id, + o.project_name, + o.developer, + o.district, + similarity(d.comm_name, o.project_name) AS name_sim, + (d.dev_norm = o.dev_norm AND d.dev_norm <> '')::int AS dev_match, + ( + coalesce(d.district_name, '') ILIKE '%' || coalesce(o.district, '') || '%' + OR coalesce(o.district, '') ILIKE '%' || coalesce(d.district_name, '') || '%' + )::int AS district_match + FROM _u_domrf d + CROSS JOIN LATERAL ( + SELECT u.project_name, u.developer, u.district, u.dev_norm + FROM _u_obj u + WHERE similarity(d.comm_name, u.project_name) > 0.3 + ORDER BY similarity(d.comm_name, u.project_name) DESC + LIMIT 3 + ) o; + +-- Rank per obj_id by composite score, pick top +WITH ranked AS ( + SELECT + obj_id, project_name, + (name_sim * 0.6 + dev_match * 0.25 + district_match * 0.15) AS composite, + ROW_NUMBER() OVER ( + PARTITION BY obj_id + ORDER BY (name_sim * 0.6 + dev_match * 0.25 + district_match * 0.15) DESC, + name_sim DESC + ) AS rn + FROM _pairs +) +INSERT INTO objective_complex_mapping + (objective_complex_name, domrf_obj_id, objective_group, + match_method, match_score, is_reviewed, note) +SELECT + r.project_name, + r.obj_id, + 'Екатеринбург', + 'multi_feature_v1', + r.composite, + false, + 'OBJ-2 multi-feature 117_fast 2026-05-17 (name 0.6 + dev 0.25 + district 0.15)' + FROM ranked r + WHERE r.rn = 1 + AND r.composite >= 0.75 +ON CONFLICT (objective_complex_name, objective_group) DO NOTHING; + +COMMIT; + +-- REFRESH MV outside tx (CONCURRENTLY requires non-tx) +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_layout_velocity;