Skip to main content

sqlite_knowledge_graph/
schema.rs

1//! Database schema creation and management.
2
3use rusqlite::Connection;
4
5use crate::error::Result;
6
7/// Create the knowledge graph schema in the database.
8pub fn create_schema(conn: &Connection) -> Result<()> {
9    let tx = conn.unchecked_transaction()?;
10
11    // Create entities table
12    tx.execute(
13        r#"
14        CREATE TABLE IF NOT EXISTS kg_entities (
15            id INTEGER PRIMARY KEY AUTOINCREMENT,
16            entity_type TEXT NOT NULL,
17            name TEXT NOT NULL,
18            properties TEXT,  -- JSON
19            created_at INTEGER DEFAULT (strftime('%s', 'now')),
20            updated_at INTEGER DEFAULT (strftime('%s', 'now'))
21        )
22        "#,
23        [],
24    )?;
25
26    // Create relations table
27    tx.execute(
28        r#"
29        CREATE TABLE IF NOT EXISTS kg_relations (
30            id INTEGER PRIMARY KEY AUTOINCREMENT,
31            source_id INTEGER NOT NULL,
32            target_id INTEGER NOT NULL,
33            rel_type TEXT NOT NULL,
34            weight REAL DEFAULT 1.0,
35            properties TEXT,  -- JSON
36            created_at INTEGER DEFAULT (strftime('%s', 'now')),
37            FOREIGN KEY (source_id) REFERENCES kg_entities(id) ON DELETE CASCADE,
38            FOREIGN KEY (target_id) REFERENCES kg_entities(id) ON DELETE CASCADE
39        )
40        "#,
41        [],
42    )?;
43
44    // Create vectors table
45    tx.execute(
46        r#"
47        CREATE TABLE IF NOT EXISTS kg_vectors (
48            entity_id INTEGER NOT NULL PRIMARY KEY,
49            vector BLOB NOT NULL,
50            dimension INTEGER NOT NULL,
51            created_at INTEGER DEFAULT (strftime('%s', 'now')),
52            FOREIGN KEY (entity_id) REFERENCES kg_entities(id) ON DELETE CASCADE
53        )
54        "#,
55        [],
56    )?;
57
58    // Create indexes
59    tx.execute(
60        "CREATE INDEX IF NOT EXISTS idx_entities_type ON kg_entities(entity_type)",
61        [],
62    )?;
63
64    tx.execute(
65        "CREATE INDEX IF NOT EXISTS idx_entities_name ON kg_entities(name)",
66        [],
67    )?;
68
69    tx.execute(
70        "CREATE INDEX IF NOT EXISTS idx_relations_source ON kg_relations(source_id)",
71        [],
72    )?;
73
74    tx.execute(
75        "CREATE INDEX IF NOT EXISTS idx_relations_target ON kg_relations(target_id)",
76        [],
77    )?;
78
79    tx.execute(
80        "CREATE INDEX IF NOT EXISTS idx_relations_type ON kg_relations(rel_type)",
81        [],
82    )?;
83
84    // Create hyperedges table (higher-order relations)
85    tx.execute(
86        r#"
87        CREATE TABLE IF NOT EXISTS kg_hyperedges (
88            id INTEGER PRIMARY KEY AUTOINCREMENT,
89            hyperedge_type TEXT NOT NULL,
90            entity_ids TEXT NOT NULL,
91            weight REAL DEFAULT 1.0,
92            arity INTEGER NOT NULL,
93            properties TEXT,
94            created_at INTEGER DEFAULT (strftime('%s', 'now')),
95            updated_at INTEGER DEFAULT (strftime('%s', 'now'))
96        )
97        "#,
98        [],
99    )?;
100
101    // Create hyperedge-entity association table
102    tx.execute(
103        r#"
104        CREATE TABLE IF NOT EXISTS kg_hyperedge_entities (
105            hyperedge_id INTEGER NOT NULL,
106            entity_id INTEGER NOT NULL,
107            position INTEGER NOT NULL,
108            PRIMARY KEY (hyperedge_id, entity_id),
109            FOREIGN KEY (hyperedge_id) REFERENCES kg_hyperedges(id) ON DELETE CASCADE,
110            FOREIGN KEY (entity_id) REFERENCES kg_entities(id) ON DELETE CASCADE
111        )
112        "#,
113        [],
114    )?;
115
116    // Hyperedge indexes
117    tx.execute(
118        "CREATE INDEX IF NOT EXISTS idx_hyperedges_type ON kg_hyperedges(hyperedge_type)",
119        [],
120    )?;
121    tx.execute(
122        "CREATE INDEX IF NOT EXISTS idx_hyperedges_arity ON kg_hyperedges(arity)",
123        [],
124    )?;
125    tx.execute(
126        "CREATE INDEX IF NOT EXISTS idx_he_entities_entity ON kg_hyperedge_entities(entity_id)",
127        [],
128    )?;
129    tx.execute(
130        "CREATE INDEX IF NOT EXISTS idx_he_entities_hyperedge ON kg_hyperedge_entities(hyperedge_id)",
131        [],
132    )?;
133
134    tx.commit()?;
135    Ok(())
136}
137
138/// Check if the schema exists.
139pub fn schema_exists(conn: &Connection) -> Result<bool> {
140    let mut stmt = conn
141        .prepare("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='kg_entities'")?;
142    let count: i64 = stmt.query_row([], |row| row.get(0))?;
143    Ok(count > 0)
144}