Files
ArchiTools/prisma/postgis-setup.sql
T
AI Assistant b33fe35c4b perf(martin+postgres): connection pool limit + compound indexes + minzoom alignment
- Martin: pool_size=8 (prevents overwhelming PostgreSQL with concurrent queries)
- Martin: gis_terenuri minzoom 10→14, gis_cladiri minzoom 12→15
  (PMTiles serves z0-z14, no point in Martin generating those)
- PostGIS: add compound index layerId+geom for Martin view queries
- PostGIS: add B-tree index on layerId for LIKE filtering in views

Fixes 90% CPU on PostgreSQL during cold tile loads at detail zoom levels.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 15:15:13 +02:00

126 lines
4.3 KiB
PL/PgSQL

-- =============================================================================
-- PostGIS native geometry setup for GisFeature
-- Run once via POST /api/eterra/setup-postgis (idempotent — safe to re-run)
--
-- What this does:
-- 1. Ensures PostGIS extension
-- 2. Adds native geometry column (geom) if missing
-- 3. Creates trigger to auto-convert GeoJSON → native on INSERT/UPDATE
-- 4. Backfills existing features that have JSON geometry but no native geom
-- 5. Creates GiST spatial index for fast spatial queries
-- 6. Creates QGIS-friendly views with clean column names
-- =============================================================================
-- 1. Ensure PostGIS extension
CREATE EXTENSION IF NOT EXISTS postgis;
-- 2. Add native geometry column (idempotent)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'GisFeature' AND column_name = 'geom'
) THEN
ALTER TABLE "GisFeature" ADD COLUMN geom geometry(Geometry, 3844);
END IF;
END $$;
-- 3. Trigger function: auto-convert GeoJSON (geometry JSON column) → native PostGIS (geom)
CREATE OR REPLACE FUNCTION gis_feature_sync_geom()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.geometry IS NOT NULL THEN
BEGIN
NEW.geom := ST_SetSRID(ST_GeomFromGeoJSON(NEW.geometry::text), 3844);
EXCEPTION WHEN OTHERS THEN
-- Invalid GeoJSON → leave geom NULL rather than fail the write
NEW.geom := NULL;
END;
ELSE
NEW.geom := NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 4. Attach trigger (drop + recreate for idempotency)
DROP TRIGGER IF EXISTS trg_gis_feature_sync_geom ON "GisFeature";
CREATE TRIGGER trg_gis_feature_sync_geom
BEFORE INSERT OR UPDATE OF geometry ON "GisFeature"
FOR EACH ROW
EXECUTE FUNCTION gis_feature_sync_geom();
-- 5. Backfill: convert existing JSON geometries to native
UPDATE "GisFeature"
SET geom = ST_SetSRID(ST_GeomFromGeoJSON(geometry::text), 3844)
WHERE geometry IS NOT NULL AND geom IS NULL;
-- 6. GiST spatial index for fast bounding-box / intersection queries
CREATE INDEX IF NOT EXISTS gis_feature_geom_idx
ON "GisFeature" USING GIST (geom);
-- Compound index: layerId + spatial — Martin queries filter by layer via views
CREATE INDEX IF NOT EXISTS gis_feature_layer_geom_idx
ON "GisFeature" ("layerId", geom) WHERE geom IS NOT NULL;
-- B-tree index on layerId for view filtering (LIKE 'TERENURI%', 'CLADIRI%')
CREATE INDEX IF NOT EXISTS gis_feature_layer_id_idx
ON "GisFeature" ("layerId");
-- =============================================================================
-- 7. QGIS-friendly views
-- - Clean snake_case column names
-- - Only rows with valid geometry
-- - One master view + per-category views
-- =============================================================================
-- Master view: all features
CREATE OR REPLACE VIEW gis_features AS
SELECT
id,
"layerId" AS layer_id,
siruta,
"objectId" AS object_id,
"inspireId" AS inspire_id,
"cadastralRef" AS cadastral_ref,
"areaValue" AS area_value,
"isActive" AS is_active,
attributes,
enrichment,
"enrichedAt" AS enriched_at,
"projectId" AS project_id,
"createdAt" AS created_at,
"updatedAt" AS updated_at,
geom
FROM "GisFeature"
WHERE geom IS NOT NULL;
-- Terenuri (parcels)
CREATE OR REPLACE VIEW gis_terenuri AS
SELECT * FROM gis_features
WHERE layer_id LIKE 'TERENURI%' OR layer_id LIKE 'CADGEN_LAND%';
-- Clădiri (buildings)
CREATE OR REPLACE VIEW gis_cladiri AS
SELECT * FROM gis_features
WHERE layer_id LIKE 'CLADIRI%' OR layer_id LIKE 'CADGEN_BUILDING%';
-- Documentații (expertize, zone interes, recepții)
CREATE OR REPLACE VIEW gis_documentatii AS
SELECT * FROM gis_features
WHERE layer_id LIKE 'EXPERTIZA%'
OR layer_id LIKE 'ZONE_INTERES%'
OR layer_id LIKE 'RECEPTII%';
-- Administrativ (limite UAT, intravilan, arii speciale)
CREATE OR REPLACE VIEW gis_administrativ AS
SELECT * FROM gis_features
WHERE layer_id LIKE 'LIMITE%'
OR layer_id LIKE 'SPECIAL_AREAS%';
-- =============================================================================
-- Done! QGIS connection: PostgreSQL → 10.10.10.166:5432 / architools_db
-- Add layers from views: gis_terenuri, gis_cladiri, gis_documentatii, etc.
-- SRID: 3844 (Stereo70)
-- =============================================================================