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