Neo4j Schema
Cronus uses Neo4j as its graph database. The schema is designed to be extensible - genealogy is the first use case, but the same patterns support org charts, professional networks, and other relationship graphs.
Node Types
Person
Primary entity representing an individual.
(:Person {
id: String, // UUID, primary key
given_name: String,
surname: String,
birth_date: Date,
birth_place: String,
death_date: Date,
death_place: String,
gender: String, // 'M', 'F', 'U' (unknown)
notes: String,
created_at: DateTime,
updated_at: DateTime,
source: String // 'manual', 'gedcom', 'familysearch'
})
Event
Life events (births, deaths, marriages, etc.)
(:Event {
id: String,
event_type: String, // 'birth', 'death', 'marriage', 'divorce', etc.
date: Date,
place: String,
notes: String
})
Place
Locations with optional hierarchy.
(:Place {
id: String,
name: String,
full_name: String, // "Salt Lake City, Utah, USA"
latitude: Float,
longitude: Float
})
Source
Citations and documentation.
(:Source {
id: String,
title: String,
author: String,
publication: String,
url: String,
notes: String
})
Relationship Types
Family Relationships
// Parent-child (directional)
(parent:Person)-[:PARENT_OF {
relationship_type: String // 'biological', 'adoptive', 'step', 'foster'
}]->(child:Person)
// Inverse for efficient queries
(child:Person)-[:CHILD_OF {
relationship_type: String
}]->(parent:Person)
// Spousal relationship
(person1:Person)-[:SPOUSE_OF {
marriage_date: Date,
marriage_place: String,
divorce_date: Date,
relationship_type: String // 'married', 'domestic_partner', 'engaged'
}]->(person2:Person)
Event Relationships
// Person participated in event
(person:Person)-[:PARTICIPATED_IN {
role: String // 'subject', 'witness', 'officiant'
}]->(event:Event)
// Event occurred at place
(event:Event)-[:OCCURRED_AT]->(place:Place)
Source Relationships
// Any node can be cited
(node)-[:CITED_BY {
citation_text: String,
page: String
}]->(source:Source)
Indexes
// Primary lookups
CREATE INDEX person_id FOR (p:Person) ON (p.id);
CREATE INDEX event_id FOR (e:Event) ON (e.id);
CREATE INDEX place_id FOR (pl:Place) ON (pl.id);
CREATE INDEX source_id FOR (s:Source) ON (s.id);
// Search indexes
CREATE INDEX person_surname FOR (p:Person) ON (p.surname);
CREATE INDEX person_given FOR (p:Person) ON (p.given_name);
// Full-text search
CREATE FULLTEXT INDEX person_name_search FOR (p:Person) ON EACH [p.given_name, p.surname];
Core Graph Queries
These queries work on ANY schema with parent/child relationships.
Ancestors
MATCH (p:Person {id: $person_id})-[:CHILD_OF*]->(ancestor:Person)
RETURN ancestor
Descendants
MATCH (p:Person {id: $person_id})-[:PARENT_OF*]->(descendant:Person)
RETURN descendant
Common Ancestors
MATCH (p1:Person {id: $person1_id})-[:CHILD_OF*]->(ancestor:Person)<-[:CHILD_OF*]-(p2:Person {id: $person2_id})
RETURN DISTINCT ancestor
Relationship Path
MATCH path = shortestPath(
(p1:Person {id: $person1_id})-[:PARENT_OF|CHILD_OF|SPOUSE_OF*]-(p2:Person {id: $person2_id})
)
RETURN path