Spaces:
Running
Running
| -- Enable PostGIS | |
| CREATE EXTENSION IF NOT EXISTS postgis; | |
| -- 4.2 active_conflicts (War-level rollup) | |
| CREATE TABLE active_conflicts ( | |
| conflict_id SERIAL PRIMARY KEY, | |
| name VARCHAR(200), -- 'Russo-Ukrainian War' | |
| countries TEXT[], -- ['UKR', 'RUS'] | |
| region VARCHAR(100), | |
| start_date DATE, | |
| status VARCHAR(20), -- ACTIVE | CEASEFIRE | RESOLVED | |
| intensity VARCHAR(20), -- WAR | CRISIS | DISPUTE | TENSION | |
| total_events INTEGER DEFAULT 0, | |
| last_event_at TIMESTAMP | |
| ); | |
| -- 4.1 conflict_events | |
| CREATE TABLE conflict_events ( | |
| id BIGSERIAL PRIMARY KEY, | |
| event_id VARCHAR(100) UNIQUE NOT NULL, -- CIQ-YYYYMMDD-ISO3-NNNNN | |
| source VARCHAR(20) NOT NULL, -- GDELT | RSS | RELIEFWEB | UCDP | |
| source_reliability VARCHAR(10) DEFAULT 'MEDIUM', -- HIGH | MEDIUM | LOW | |
| event_time TIMESTAMP NOT NULL, | |
| event_date DATE NOT NULL, | |
| year SMALLINT GENERATED ALWAYS AS (EXTRACT(YEAR FROM event_date)) STORED, | |
| week SMALLINT GENERATED ALWAYS AS (EXTRACT(WEEK FROM event_date)) STORED, | |
| country VARCHAR(100) NOT NULL, | |
| country_iso3 CHAR(3) NOT NULL, | |
| region VARCHAR(100), | |
| admin1 VARCHAR(100), -- Province / Oblast | |
| admin2 VARCHAR(100), -- District / Rayon | |
| city VARCHAR(100), | |
| lat DECIMAL(9,6), | |
| lon DECIMAL(10,6), | |
| geom GEOGRAPHY(POINT,4326), | |
| geo_precision SMALLINT DEFAULT 3, -- 1=city 2=district 3=country | |
| event_type VARCHAR(50), -- Battles | Explosions | Violence vs Civilians | Protests | |
| event_subtype VARCHAR(80), -- Armed clash | Air/drone strike | Shelling… | |
| interaction_code VARCHAR(60), -- Military vs Military | Military vs Civilians… | |
| actor1 VARCHAR(200), | |
| actor1_type VARCHAR(50), -- Military Forces | Rebel Group | Civilians… | |
| actor2 VARCHAR(200), | |
| actor2_type VARCHAR(50), | |
| fatalities INTEGER DEFAULT 0, | |
| fatalities_civilians INTEGER DEFAULT 0, | |
| fatalities_confidence VARCHAR(10) DEFAULT 'LOW', -- HIGH | MEDIUM | LOW | |
| severity VARCHAR(20), -- HIGH | MEDIUM | LOW | |
| severity_score DECIMAL(4,2), -- 0.00 – 10.00 | |
| title VARCHAR(500), | |
| notes TEXT, | |
| tags TEXT[], -- ['artillery','urban-combat','civilian-area'] | |
| source_url TEXT, | |
| conflict_name VARCHAR(200), -- 'Russo-Ukrainian War' | |
| conflict_id INTEGER REFERENCES active_conflicts(conflict_id), | |
| category VARCHAR(20) DEFAULT 'GENERAL', | |
| ingested_at TIMESTAMP DEFAULT NOW() | |
| ); | |
| -- INDEXES (sub-10ms guaranteed) | |
| CREATE INDEX idx_event_time ON conflict_events(event_time DESC); | |
| CREATE INDEX idx_country_time ON conflict_events(country_iso3, event_time DESC); | |
| CREATE INDEX idx_geom ON conflict_events USING GIST(geom); | |
| CREATE INDEX idx_severity ON conflict_events(severity_score DESC); | |
| CREATE INDEX idx_category ON conflict_events(category); | |
| CREATE INDEX idx_type ON conflict_events(event_type, event_time DESC); | |
| CREATE INDEX idx_tags ON conflict_events USING GIN(tags); | |
| -- We trigger a NOTIFY when a new conflict event is inserted | |
| -- This pushes data to the websocket | |
| CREATE OR REPLACE FUNCTION notify_new_conflict_event() | |
| RETURNS trigger AS $$ | |
| BEGIN | |
| PERFORM pg_notify( | |
| 'new_conflict_event', | |
| row_to_json(NEW)::text | |
| ); | |
| RETURN NEW; | |
| END; | |
| $$ LANGUAGE plpgsql; | |
| CREATE TRIGGER trigger_new_conflict_event | |
| AFTER INSERT ON conflict_events | |
| FOR EACH ROW EXECUTE FUNCTION notify_new_conflict_event(); | |
| -- 5. Intelligence Articles Hub | |
| CREATE TABLE intel_articles ( | |
| id SERIAL PRIMARY KEY, | |
| title VARCHAR(255) NOT NULL, | |
| content TEXT NOT NULL, -- Supports Markdown | |
| author VARCHAR(100), | |
| tags TEXT[], | |
| created_at TIMESTAMP DEFAULT NOW(), | |
| updated_at TIMESTAMP DEFAULT NOW(), | |
| search_vector TSVECTOR GENERATED ALWAYS AS ( | |
| to_tsvector('english', title || ' ' || content) | |
| ) STORED | |
| ); | |
| CREATE INDEX idx_articles_search ON intel_articles USING GIN(search_vector); | |
| CREATE INDEX idx_articles_created ON intel_articles(created_at DESC); | |