-- 55_relations_schema.sql -- Typed relations between entities. Replaces noisy co-mention with semantic -- edges like (Person, witnessed, Event), (Event, occurred_at, Location), -- (Person, signed, Document), etc. BEGIN; CREATE TABLE IF NOT EXISTS public.relations ( relation_pk BIGSERIAL PRIMARY KEY, source_class TEXT NOT NULL, source_id TEXT NOT NULL, relation_type TEXT NOT NULL, target_class TEXT NOT NULL, target_id TEXT NOT NULL, evidence_ref TEXT, -- e.g. '[[doc-id/p007]]' or chunk_id confidence TEXT NOT NULL DEFAULT 'medium', -- high|medium|low extracted_by TEXT NOT NULL DEFAULT 'yaml', -- yaml|regex|llm|manual created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (source_class, source_id, relation_type, target_class, target_id, evidence_ref) ); -- Enum check on relation_type (extensible — add new values as we discover them) ALTER TABLE public.relations ADD CONSTRAINT relations_type_check CHECK (relation_type IN ( 'witnessed', -- (person, witnessed, event) 'occurred_at', -- (event, occurred_at, location) 'involves_uap', -- (event, involves_uap, uap_object) 'documented_in', -- (event, documented_in, document) 'authored', -- (person, authored, document) 'signed', -- (person, signed, document) 'mentioned_by', -- (person, mentioned_by, document) 'employed_by', -- (person, employed_by, organization) 'operated_by', -- (operation, operated_by, organization) 'investigated', -- (person, investigated, event) 'commanded', -- (person, commanded, organization) 'related_to', -- generic fallback (lower-quality) 'similar_to', -- (event, similar_to, event) 'precedes', -- (event, precedes, event) 'follows' -- (event, follows, event) )); CREATE INDEX IF NOT EXISTS relations_source_idx ON public.relations (source_class, source_id); CREATE INDEX IF NOT EXISTS relations_target_idx ON public.relations (target_class, target_id); CREATE INDEX IF NOT EXISTS relations_type_idx ON public.relations (relation_type); COMMIT;