Relix

Problem solving

Family history

Graph + Time

The problem

"From a family tree stored as parent links, list a person's ancestors, say how many generations back each one is, and show when each was born."

Classifying it

A parent-pointer tree is the same shape as the fraud graph and the bill of materials — this is "follow the edge to any depth", and the operator is CLOSURE (or FIX when the step needs more than two columns; see recursion).

The data

People := [
| id | name | born |
|----|------|------|
| 1  | Ada  | 1900 |
| 2  | Bob  | 1925 |
| 3  | Cara | 1952 |
| 4  | Dan  | 1978 |
| 5  | Cy   | 1955 |
];

ParentOf := [
| child | parent |
|-------|--------|
| 2     | 1      |
| 3     | 2      |
| 4     | 3      |
| 5     | 2      |
];

Dan (4) descends from Cara (3), Bob (2), Ada (1); Cy (5) is Bob's other child — Dan's uncle.

Stage 1: the ancestors (graph — reachability)

CLOSURE child, parent follows the parent edge to any depth. Scope it to Dan:

Query
Ancestors := { CLOSURE child, parent (ParentOf) };
query { π parent → ancestor (σ child = 4 (Ancestors)) };
Result
 ancestor
 ────────
        3
        2
        1
(3 rows)

Stage 2: how many generations back (graph — distance)

PATH stamps each ancestor with its distance, so "grandparent" is generation 2:

Query
query {
    τ gen, ancestor (
        π parent → ancestor, gen (
            σ child = 4 (PATH child, parent HOPS 1 TO 5 AS gen (ParentOf))))
};
Result
 ancestor  gen
 ────────  ───
        3    1
        2    2
        1    3
(3 rows)

Stage 3: with birth years (time)

Join the ancestor set back to People — an ordinary existence semi-join keeps each ancestor once and brings the temporal column along:

Query
DanAncestors := { δ (π parent → id (σ child = 4 (Ancestors))) };
query { τ born, name (People ⋉ People.id = DanAncestors.id DanAncestors) };
Result
 id  name  born
 ──  ────  ────
  1  Ada   1900
  2  Bob   1925
  3  Cara  1952
(3 rows)

What this shows

The graph is the whole problem; time is a column you pick up at the end. Note that the sibling line (Cy) never appears — reachability up the parent edge is exactly ancestors, not relatives. For descendants, run the closure the other way (CLOSURE parent, child); for cousins and the rest, CLUSTER the undirected "related-to" graph, which is the connected-groups recipe.

Two edges from a real GEDCOM would change nothing here: the connector produces the same People and ParentOf relations, and dates arrive as real DATE/TIMESTAMP values, so "ancestors born before 1930" is a σ on the temporal column.

Recipes drawn on