Schema update
This commit is contained in:
@@ -2,21 +2,24 @@
|
||||
Neo4j Unified Knowledge Graph Schema Initialization
|
||||
=====================================================
|
||||
Creates the foundational schema for a unified knowledge graph used by
|
||||
fourteen AI assistants across three teams:
|
||||
sixteen AI assistants across three teams:
|
||||
|
||||
Personal Team:
|
||||
Hypatia (Learning), Marcus (Fitness), Seneca (Reflection),
|
||||
Nate (Travel), Bowie (Culture), Bourdain (Food),
|
||||
Cousteau (Nature), Garth (Finance), Cristiano (Football)
|
||||
Personal Team (Iolaus):
|
||||
Shawn (General — calendar/contacts/comms), Nate (Travel),
|
||||
Hypatia (Learning), Marcus (Fitness),
|
||||
Watson (Reflection & Emotional Safety), Bourdain (Food),
|
||||
David (Arts & Culture), Cousteau (Nature), Garth (Finance),
|
||||
Cristiano (Football)
|
||||
|
||||
Work Team:
|
||||
Alan (Strategy), Ann (Marketing), Jeffrey (Sales), Jarvis (Execution)
|
||||
Work Team (Mentor):
|
||||
Alan (Strategy), Ann (Marketing), Jeffrey (Sales),
|
||||
Jarvis (Execution), AWS SA (Architecture)
|
||||
|
||||
Engineering Team:
|
||||
Engineering Team (Kottos):
|
||||
Scotty (Infrastructure), Harper (Prototyping)
|
||||
|
||||
Schema Reference:
|
||||
docs/neo4j-unified-schema.md
|
||||
docs/neo4j-unified-schema.md (v2.3.0)
|
||||
|
||||
Requirements:
|
||||
pip install neo4j
|
||||
@@ -72,7 +75,7 @@ class LifeGraphSchema:
|
||||
"""
|
||||
Create uniqueness constraints on key node properties.
|
||||
This ensures data integrity and creates indexes automatically.
|
||||
All 74 node types get an id uniqueness constraint.
|
||||
All 79 node types get an id uniqueness constraint.
|
||||
"""
|
||||
constraints = [
|
||||
# ── Universal nodes ──────────────────────────────────────
|
||||
@@ -101,12 +104,16 @@ class LifeGraphSchema:
|
||||
"CREATE CONSTRAINT personalrecord_id IF NOT EXISTS FOR (n:PersonalRecord) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT bodymetric_id IF NOT EXISTS FOR (n:BodyMetric) REQUIRE n.id IS UNIQUE",
|
||||
|
||||
# ── Seneca: Reflection & Wellness ────────────────────────
|
||||
# ── Watson: Reflection & Emotional Safety ────────────────
|
||||
"CREATE CONSTRAINT reflection_id IF NOT EXISTS FOR (n:Reflection) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT value_id IF NOT EXISTS FOR (n:Value) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT habit_id IF NOT EXISTS FOR (n:Habit) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT lifeevent_id IF NOT EXISTS FOR (n:LifeEvent) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT intention_id IF NOT EXISTS FOR (n:Intention) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT emotionalmemory_id IF NOT EXISTS FOR (n:EmotionalMemory) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT relationshiptheme_id IF NOT EXISTS FOR (n:RelationshipTheme) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT dialoguenote_id IF NOT EXISTS FOR (n:DialogueNote) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT dynamicpattern_id IF NOT EXISTS FOR (n:DynamicPattern) REQUIRE n.id IS UNIQUE",
|
||||
|
||||
# ── Bourdain: Food & Cooking ─────────────────────────────
|
||||
"CREATE CONSTRAINT recipe_id IF NOT EXISTS FOR (n:Recipe) REQUIRE n.id IS UNIQUE",
|
||||
@@ -115,7 +122,7 @@ class LifeGraphSchema:
|
||||
"CREATE CONSTRAINT meal_id IF NOT EXISTS FOR (n:Meal) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT technique_id IF NOT EXISTS FOR (n:Technique) REQUIRE n.id IS UNIQUE",
|
||||
|
||||
# ── Bowie: Arts & Culture ────────────────────────────────
|
||||
# ── David: Arts & Culture ────────────────────────────────
|
||||
"CREATE CONSTRAINT music_id IF NOT EXISTS FOR (n:Music) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT film_id IF NOT EXISTS FOR (n:Film) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT artwork_id IF NOT EXISTS FOR (n:Artwork) REQUIRE n.id IS UNIQUE",
|
||||
@@ -146,7 +153,10 @@ class LifeGraphSchema:
|
||||
"CREATE CONSTRAINT tournament_id IF NOT EXISTS FOR (n:Tournament) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT player_id IF NOT EXISTS FOR (n:Player) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT season_id IF NOT EXISTS FOR (n:Season) REQUIRE n.id IS UNIQUE",
|
||||
|
||||
|
||||
# ── Shawn: Personal General Assistant ────────────────────
|
||||
"CREATE CONSTRAINT communication_id IF NOT EXISTS FOR (n:Communication) REQUIRE n.id IS UNIQUE",
|
||||
|
||||
# ── Work: Business ───────────────────────────────────────
|
||||
"CREATE CONSTRAINT client_id IF NOT EXISTS FOR (n:Client) REQUIRE n.id IS UNIQUE",
|
||||
"CREATE CONSTRAINT contact_id IF NOT EXISTS FOR (n:Contact) REQUIRE n.id IS UNIQUE",
|
||||
@@ -258,6 +268,10 @@ class LifeGraphSchema:
|
||||
"CREATE INDEX lifeevent_date IF NOT EXISTS FOR (n:LifeEvent) ON (n.date)",
|
||||
"CREATE INDEX intention_date IF NOT EXISTS FOR (n:Intention) ON (n.date)",
|
||||
"CREATE INDEX match_date IF NOT EXISTS FOR (n:Match) ON (n.date)",
|
||||
"CREATE INDEX emotionalmemory_date IF NOT EXISTS FOR (n:EmotionalMemory) ON (n.date)",
|
||||
"CREATE INDEX dialoguenote_date IF NOT EXISTS FOR (n:DialogueNote) ON (n.date)",
|
||||
"CREATE INDEX dynamicpattern_date IF NOT EXISTS FOR (n:DynamicPattern) ON (n.date)",
|
||||
"CREATE INDEX communication_date IF NOT EXISTS FOR (n:Communication) ON (n.date)",
|
||||
|
||||
# ── Type / Status / Category indexes ─────────────────────
|
||||
"CREATE INDEX event_type IF NOT EXISTS FOR (n:Event) ON (n.type)",
|
||||
@@ -304,6 +318,8 @@ class LifeGraphSchema:
|
||||
"CREATE INDEX goal_domain IF NOT EXISTS FOR (n:Goal) ON (n.domain)",
|
||||
"CREATE INDEX location_domain IF NOT EXISTS FOR (n:Location) ON (n.domain)",
|
||||
"CREATE INDEX person_domain IF NOT EXISTS FOR (n:Person) ON (n.domain)",
|
||||
"CREATE INDEX contact_domain IF NOT EXISTS FOR (n:Contact) ON (n.domain)",
|
||||
"CREATE INDEX task_domain IF NOT EXISTS FOR (n:Task) ON (n.domain)",
|
||||
]
|
||||
|
||||
with self.driver.session() as session:
|
||||
@@ -426,16 +442,33 @@ class LifeGraphSchema:
|
||||
"SHOW INDEXES WHERE name = 'client_status'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
# Cristiano team sample
|
||||
("Constraint: Match",
|
||||
("Constraint: Match",
|
||||
"SHOW CONSTRAINTS WHERE name = 'match_id'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
("Constraint: Team",
|
||||
("Constraint: Team",
|
||||
"SHOW CONSTRAINTS WHERE name = 'team_id'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
# Total constraint count (74 node types)
|
||||
("Total constraints >= 74",
|
||||
# Watson emotional-memory samples (v2.2.0)
|
||||
("Constraint: EmotionalMemory",
|
||||
"SHOW CONSTRAINTS WHERE name = 'emotionalmemory_id'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
("Constraint: RelationshipTheme",
|
||||
"SHOW CONSTRAINTS WHERE name = 'relationshiptheme_id'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
("Constraint: DialogueNote",
|
||||
"SHOW CONSTRAINTS WHERE name = 'dialoguenote_id'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
("Constraint: DynamicPattern",
|
||||
"SHOW CONSTRAINTS WHERE name = 'dynamicpattern_id'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
# Shawn sample (v2.3.0)
|
||||
("Constraint: Communication",
|
||||
"SHOW CONSTRAINTS WHERE name = 'communication_id'",
|
||||
lambda r: len(list(r)) == 1),
|
||||
# Total constraint count (79 node types as of v2.3.0)
|
||||
("Total constraints >= 79",
|
||||
"SHOW CONSTRAINTS",
|
||||
lambda r: len(list(r)) >= 74),
|
||||
lambda r: len(list(r)) >= 79),
|
||||
]
|
||||
|
||||
if include_schema_tests:
|
||||
@@ -523,7 +556,7 @@ class LifeGraphSchema:
|
||||
RETURN b.id AS id
|
||||
"""),
|
||||
|
||||
# ── Personal: Sample goal (Seneca) ───────────────────────
|
||||
# ── Personal: Sample goal (Watson) ───────────────────────
|
||||
("Goal:goal_sample_2025", """
|
||||
MERGE (g:Goal {id: 'goal_sample_2025'})
|
||||
ON CREATE SET g.created_at = datetime()
|
||||
@@ -556,6 +589,40 @@ class LifeGraphSchema:
|
||||
RETURN a.id AS id
|
||||
"""),
|
||||
|
||||
# ── Shawn: Sample personal contact + communication ───────
|
||||
("Contact:contact_sample_personal", """
|
||||
MERGE (c:Contact {id: 'contact_sample_personal'})
|
||||
ON CREATE SET c.created_at = datetime()
|
||||
SET c.name = 'Sample Personal Contact',
|
||||
c.domain = 'personal',
|
||||
c.relationship_strength = 'developing',
|
||||
c.updated_at = datetime()
|
||||
RETURN c.id AS id
|
||||
"""),
|
||||
|
||||
("Communication:comm_sample", """
|
||||
MERGE (c:Communication {id: 'comm_sample'})
|
||||
ON CREATE SET c.created_at = datetime()
|
||||
SET c.type = 'in_person',
|
||||
c.contact_id = 'contact_sample_personal',
|
||||
c.date = date(),
|
||||
c.summary = 'Sample interaction record',
|
||||
c.updated_at = datetime()
|
||||
RETURN c.id AS id
|
||||
"""),
|
||||
|
||||
# ── Watson: Sample emotional memory ──────────────────────
|
||||
("EmotionalMemory:memory_sample", """
|
||||
MERGE (m:EmotionalMemory {id: 'memory_sample'})
|
||||
ON CREATE SET m.created_at = datetime()
|
||||
SET m.date = date(),
|
||||
m.theme = 'safety',
|
||||
m.intensity = 3,
|
||||
m.content = 'Sample emotional memory entry',
|
||||
m.updated_at = datetime()
|
||||
RETURN m.id AS id
|
||||
"""),
|
||||
|
||||
# ── Work: Sample client ──────────────────────────────────
|
||||
("Client:client_sample_corp", """
|
||||
MERGE (c:Client {id: 'client_sample_corp'})
|
||||
@@ -563,6 +630,7 @@ class LifeGraphSchema:
|
||||
SET c.name = 'Sample Corp',
|
||||
c.industry = 'Technology',
|
||||
c.status = 'prospect',
|
||||
c.domain = 'work',
|
||||
c.updated_at = datetime()
|
||||
RETURN c.id AS id
|
||||
"""),
|
||||
@@ -642,6 +710,8 @@ class LifeGraphSchema:
|
||||
("PURSUING", "Person", "user_main", "Goal", "goal_sample_2025"),
|
||||
("EXPLORES", "Book", "book_meditations_aurelius", "Topic", "topic_stoicism"),
|
||||
("OWNS", "Person", "user_main", "Account", "account_tfsa_sample"),
|
||||
("HAD", "Person", "user_main", "Communication", "comm_sample"),
|
||||
("WITH", "Communication", "comm_sample", "Contact", "contact_sample_personal"),
|
||||
]
|
||||
|
||||
created_rels = 0
|
||||
@@ -688,31 +758,36 @@ Event Significant occurrences (celebrations, conferences)
|
||||
Topic Subjects of interest (stoicism, AI in CX)
|
||||
Goal Objectives (personal growth, career, fitness, financial)
|
||||
|
||||
PERSONAL TEAM:
|
||||
PERSONAL TEAM (Iolaus):
|
||||
────────────────────────────────────────────────────────────────
|
||||
Shawn (General) Contact, Event, Task (all domain='personal'), Communication
|
||||
Nate (Travel) Trip, Destination, Activity
|
||||
Hypatia (Learning) Book, Author, LearningPath, Concept, Quote
|
||||
Marcus (Fitness) Training, Exercise, Program, PersonalRecord, BodyMetric
|
||||
Seneca (Reflection) Reflection, Value, Habit, LifeEvent, Intention
|
||||
Watson (Reflection) Reflection, Value, Habit, LifeEvent, Intention,
|
||||
EmotionalMemory, RelationshipTheme, DialogueNote, DynamicPattern
|
||||
Bourdain (Food) Recipe, Restaurant, Ingredient, Meal, Technique
|
||||
Bowie (Culture) Music, Film, Artwork, Playlist, Artist, Style
|
||||
David (Culture) Music, Film, Artwork, Playlist, Artist, Style
|
||||
Cousteau (Nature) Species, Plant, Tank, Garden, Ecosystem, Observation
|
||||
Garth (Finance) Account, Investment, Asset, Liability, Budget, FinancialGoal
|
||||
Cristiano (Football) Match, Team, League, Tournament, Player, Season
|
||||
|
||||
WORK TEAM:
|
||||
WORK TEAM (Mentor):
|
||||
────────────────────────────────────────────────────────────────
|
||||
Alan (Strategy) Client, Vendor, Competitor, MarketTrend, Technology, Decision
|
||||
Ann (Marketing) Content, Publication, Topic, Event
|
||||
Jeffrey (Sales) Contact, Opportunity, Proposal, Meeting
|
||||
Jarvis (Execution) Task, Meeting, Note, Decision, Project
|
||||
Ann (Marketing) Content, Publication, Topic, Event (domain='work')
|
||||
Jeffrey (Sales) Contact (domain='work'), Opportunity, Proposal, Meeting
|
||||
Jarvis (Execution) Task (domain='work'), Meeting, Note, Decision, Project
|
||||
AWS SA (Architecture) No domain ownership — writes Note (messages) only
|
||||
|
||||
ENGINEERING TEAM:
|
||||
ENGINEERING TEAM (Kottos):
|
||||
────────────────────────────────────────────────────────────────
|
||||
Scotty (Infra) Infrastructure, Incident
|
||||
Harper (Hacking) Prototype, Experiment
|
||||
|
||||
TOTAL: 74 node types, all with id uniqueness constraints
|
||||
TOTAL: 79 node types, 16 assistants. All node types have id uniqueness
|
||||
constraints. Contact/Event/Task are Universal with a `domain` field
|
||||
('personal' or 'work') disambiguating Shawn vs. Jarvis/Jeffrey ownership.
|
||||
|
||||
CROSS-TEAM CONNECTIONS (examples):
|
||||
────────────────────────────────────────────────────────────────
|
||||
@@ -723,8 +798,9 @@ Infrastructure -[HOSTS]-> Project (Engineering ↔ Work)
|
||||
Prototype -[SUPPORTS]-> Opportunity (Engineering ↔ Work)
|
||||
Project -[GENERATES_REVENUE]-> Account (Work ↔ Personal)
|
||||
Training -[BUILDS]-> Skill (Personal ↔ Work)
|
||||
Communication -[WITH]-> Contact (Shawn: personal interaction history)
|
||||
|
||||
Full schema: docs/neo4j-unified-schema.md
|
||||
Full schema: docs/neo4j-unified-schema.md (v2.3.0)
|
||||
════════════════════════════════════════════════════════════════
|
||||
"""
|
||||
print(schema_doc)
|
||||
@@ -858,7 +934,7 @@ def main():
|
||||
schema.document_schema()
|
||||
|
||||
# Create constraints (includes automatic indexes)
|
||||
logger.info("Creating constraints (74 node types)...")
|
||||
logger.info("Creating constraints (79 node types)...")
|
||||
schema.create_constraints()
|
||||
|
||||
# Create additional indexes
|
||||
@@ -886,7 +962,7 @@ def main():
|
||||
|
||||
if test_success:
|
||||
logger.info("✓ All tests passed!")
|
||||
logger.info("\nUnified graph ready for all 15 assistants.")
|
||||
logger.info("\nUnified graph ready for all 16 assistants.")
|
||||
logger.info("Schema reference: docs/neo4j-unified-schema.md")
|
||||
logger.info("\nNext steps:")
|
||||
logger.info(" 1. Import data (Plex, Calibre, etc.)")
|
||||
|
||||
@@ -35,11 +35,12 @@ EXPECTED_CONSTRAINTS = [
|
||||
"book_id", "author_id", "learningpath_id", "concept_id", "quote_id",
|
||||
# Marcus
|
||||
"training_id", "exercise_id", "program_id", "personalrecord_id", "bodymetric_id",
|
||||
# Seneca
|
||||
# Watson (Reflection & Emotional Safety)
|
||||
"reflection_id", "value_id", "habit_id", "lifeevent_id", "intention_id",
|
||||
"emotionalmemory_id", "relationshiptheme_id", "dialoguenote_id", "dynamicpattern_id",
|
||||
# Bourdain
|
||||
"recipe_id", "restaurant_id", "ingredient_id", "meal_id", "technique_id",
|
||||
# Bowie
|
||||
# David (Arts & Culture)
|
||||
"music_id", "film_id", "artwork_id", "playlist_id", "artist_id", "style_id",
|
||||
# Cousteau
|
||||
"species_id", "plant_id", "tank_id", "garden_id", "ecosystem_id", "observation_id",
|
||||
@@ -47,6 +48,8 @@ EXPECTED_CONSTRAINTS = [
|
||||
"account_id", "investment_id", "asset_id", "liability_id", "budget_id", "financialgoal_id",
|
||||
# Cristiano
|
||||
"match_id", "team_id", "league_id", "tournament_id", "player_id", "season_id",
|
||||
# Shawn (Personal General Assistant)
|
||||
"communication_id",
|
||||
# Work: Business
|
||||
"client_id", "contact_id", "opportunity_id", "proposal_id", "project_id",
|
||||
# Work: Market Intelligence
|
||||
@@ -68,11 +71,13 @@ EXPECTED_LABELS = {
|
||||
"Book", "Author", "LearningPath", "Concept", "Quote",
|
||||
"Training", "Exercise", "Program", "PersonalRecord", "BodyMetric",
|
||||
"Reflection", "Value", "Habit", "LifeEvent", "Intention",
|
||||
"EmotionalMemory", "RelationshipTheme", "DialogueNote", "DynamicPattern",
|
||||
"Recipe", "Restaurant", "Ingredient", "Meal", "Technique",
|
||||
"Music", "Film", "Artwork", "Playlist", "Artist", "Style",
|
||||
"Species", "Plant", "Tank", "Garden", "Ecosystem", "Observation",
|
||||
"Account", "Investment", "Asset", "Liability", "Budget", "FinancialGoal",
|
||||
"Match", "Team", "League", "Tournament", "Player", "Season",
|
||||
"Communication",
|
||||
"Client", "Contact", "Opportunity", "Proposal", "Project",
|
||||
"Vendor", "Competitor", "MarketTrend", "Technology",
|
||||
"Content", "Publication",
|
||||
@@ -90,6 +95,9 @@ EXPECTED_SAMPLE_NODES = [
|
||||
("Topic", "topic_stoicism"),
|
||||
("Topic", "topic_ai_in_cx"),
|
||||
("Account", "account_tfsa_sample"),
|
||||
("Contact", "contact_sample_personal"),
|
||||
("Communication", "comm_sample"),
|
||||
("EmotionalMemory", "memory_sample"),
|
||||
("Client", "client_sample_corp"),
|
||||
("Skill", "skill_cx_strategy"),
|
||||
("Infrastructure", "infra_neo4j_prod"),
|
||||
@@ -102,6 +110,8 @@ EXPECTED_SAMPLE_RELS = [
|
||||
("Person", "user_main", "PURSUING", "Goal", "goal_sample_2025"),
|
||||
("Book", "book_meditations_aurelius", "EXPLORES", "Topic", "topic_stoicism"),
|
||||
("Person", "user_main", "OWNS", "Account", "account_tfsa_sample"),
|
||||
("Person", "user_main", "HAD", "Communication", "comm_sample"),
|
||||
("Communication", "comm_sample", "WITH", "Contact", "contact_sample_personal"),
|
||||
]
|
||||
|
||||
# A sampling of expected indexes (not exhaustive, just key ones to spot-check)
|
||||
@@ -109,6 +119,7 @@ EXPECTED_INDEX_SAMPLES = [
|
||||
"person_name", "book_title", "client_name", "event_date",
|
||||
"training_date", "client_status", "task_status", "event_domain",
|
||||
"team_name", "player_name", "match_competition",
|
||||
"contact_domain", "task_domain",
|
||||
]
|
||||
|
||||
|
||||
@@ -157,7 +168,7 @@ def validate(driver, uri):
|
||||
print("═" * 65)
|
||||
print(" VALIDATION REPORT — Koios Unified Knowledge Graph")
|
||||
print("═" * 65)
|
||||
print(f" Schema Version: 2.1.0")
|
||||
print(f" Schema Version: 2.3.0")
|
||||
print(f" Database: {uri}")
|
||||
print(f" Timestamp: {now}")
|
||||
print("═" * 65)
|
||||
|
||||
Reference in New Issue
Block a user