diff --git a/backend/src/main/resources/db/migration/V54__add_family_network.sql b/backend/src/main/resources/db/migration/V54__add_family_network.sql new file mode 100644 index 00000000..0de01f91 --- /dev/null +++ b/backend/src/main/resources/db/migration/V54__add_family_network.sql @@ -0,0 +1,30 @@ +-- Family network: marks a Person as a tree node and stores typed relationships +-- between two persons. The tree page (/stammbaum) only shows persons with +-- family_member = TRUE. Symmetric types (SPOUSE_OF, SIBLING_OF) are stored once; +-- the partial unique index keeps SIBLING_OF pairs from being duplicated in the +-- reverse direction. + +ALTER TABLE persons + ADD COLUMN family_member BOOLEAN NOT NULL DEFAULT FALSE; + +CREATE TABLE person_relationships ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + person_id UUID NOT NULL REFERENCES persons(id) ON DELETE CASCADE, + related_person_id UUID NOT NULL REFERENCES persons(id) ON DELETE CASCADE, + relation_type VARCHAR(30) NOT NULL, + from_year INTEGER, + to_year INTEGER, + notes VARCHAR(2000), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT no_self_rel CHECK (person_id <> related_person_id), + CONSTRAINT unique_rel UNIQUE (person_id, related_person_id, relation_type) +); + +CREATE INDEX idx_person_rel_person_id ON person_relationships(person_id); +CREATE INDEX idx_person_rel_related_person_id ON person_relationships(related_person_id); + +-- Symmetric SIBLING_OF: enforce only one row per unordered pair. +CREATE UNIQUE INDEX unique_sibling_pair ON person_relationships ( + LEAST(person_id, related_person_id), + GREATEST(person_id, related_person_id) +) WHERE relation_type = 'SIBLING_OF';