Pohon keluarga (family tree) bukanlah struktur pohon biner sederhana. Dalam kenyataan riil genealogi (seperti pada sistem Kinova), kita menghadapi berbagai kasus relasi data yang rumit:

  • Seorang anak memiliki 2 orang tua kandung (ayah & ibu), bukan 1 parent.
  • Pernikahan ganda dan saudara tiri (half-siblings).
  • Adopsi dan pernikahan antar keluarga jauh (cross-branch lineages).

Menyimpan relasi ini dengan kolom sederhana father_id dan mother_id pada tabel orang (persons) sering kali menyebabkan table lock dan kueri join bertingkat yang sangat lambat.

1. Skema Relasi: Entities vs Unions

Pendekatan terbaik dalam database relasional seperti PostgreSQL adalah memisahkan entitas individu (Persons) dari entitas ikatan keluarga (Family Unions / Marriages):

erDiagram
    PERSONS ||--o{ UNION_MEMBERS : spouse
    UNIONS ||--o{ UNION_MEMBERS : includes
    UNIONS ||--o{ PERSONS : parent_of

    PERSONS {
        uuid id PK
        string full_name
        string gender
        date birth_date
    }
    UNIONS {
        uuid id PK
        uuid union_type
        date marriage_date
    }
    UNION_MEMBERS {
        uuid union_id FK
        uuid person_id FK
        string role
    }

2. Kueri Penelusuran Cepat dengan Recursive CTE

Untuk mengambil seluruh garis keturunan ke bawah (descendants) atau garis leluhur ke atas (ancestors) sampai $N$-generasi dalam satu kali kueri SQL, kita memanfaatkan fitur PostgreSQL WITH RECURSIVE (Common Table Expression):

WITH RECURSIVE family_lineage AS (
    -- Base member (Anchor): Leluhur awal
    SELECT id, full_name, 1 AS generation_level
    FROM persons
    WHERE id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'

    UNION ALL

    -- Recursive member: Telusuri anak-anak dari tiap generasi
    SELECT p.id, p.full_name, fl.generation_level + 1
    FROM persons p
    INNER JOIN unions u ON p.parent_union_id = u.id
    INNER JOIN union_members um ON um.union_id = u.id
    INNER JOIN family_lineage fl ON fl.id = um.person_id
)
SELECT * FROM family_lineage ORDER BY generation_level ASC;

3. Optimasi Indexing & Performa

  • Covering Index: Pasang B-Tree index pada (parent_union_id, id) untuk mempercepat penggabungan rekursif.
  • Cycle Prevention: Gunakan klausa CYCLE id SET is_cycle USING path bawaan PostgreSQL 14+ untuk mencegah infinite loop jika terjadi anomali siklus pada data keluarga.

Referensi Dokumentasi Resmi: