@@ -46,6 +46,8 @@ struct GraphNode {
4646 offset : Option < usize > ,
4747 #[ serde( rename = "hiddenCount" , skip_serializing_if = "Option::is_none" ) ]
4848 hidden_count : Option < usize > ,
49+ #[ serde( rename = "colorKey" , skip_serializing_if = "Option::is_none" ) ]
50+ color_key : Option < String > ,
4951}
5052
5153#[ derive( Debug , Clone , Serialize , Deserialize ) ]
@@ -410,6 +412,7 @@ fn graph_node_from_value(val: &Value) -> Option<GraphNode> {
410412 expand_node_id : None ,
411413 offset : None ,
412414 hidden_count : None ,
415+ color_key : None ,
413416 } )
414417}
415418
@@ -516,6 +519,7 @@ fn make_expander_node(parent_id: &str, hidden_count: usize, offset: usize) -> Gr
516519 expand_node_id : Some ( parent_id. to_string ( ) ) ,
517520 offset : Some ( offset) ,
518521 hidden_count : Some ( hidden_count) ,
522+ color_key : None ,
519523 }
520524}
521525
@@ -1071,6 +1075,7 @@ fn compute_cluster_levels(
10711075 expand_node_id : None ,
10721076 offset : None ,
10731077 hidden_count : None ,
1078+ color_key : None ,
10741079 } )
10751080 . collect ( ) ;
10761081 let cluster_node_index: HashMap < String , usize > = cluster_nodes
@@ -1298,6 +1303,7 @@ fn collect_edge_graph(conn: &Connection, limit: usize) -> Result<GraphData, Stri
12981303 expand_node_id : None ,
12991304 offset : None ,
13001305 hidden_count : None ,
1306+ color_key : None ,
13011307 } ,
13021308 ) ;
13031309 }
@@ -1319,6 +1325,130 @@ fn collect_edge_graph(conn: &Connection, limit: usize) -> Result<GraphData, Stri
13191325 ) )
13201326}
13211327
1328+ fn escaped_identifier ( value : & str ) -> String {
1329+ value. replace ( '\\' , "\\ \\ " ) . replace ( '\'' , "\\ '" )
1330+ }
1331+
1332+ fn table_properties ( conn : & Connection , table_name : & str ) -> Vec < ( String , String , bool ) > {
1333+ let Ok ( mut result) = conn. query ( & format ! (
1334+ "CALL TABLE_INFO('{}') RETURN *;" ,
1335+ escaped_identifier( table_name)
1336+ ) ) else {
1337+ return Vec :: new ( ) ;
1338+ } ;
1339+
1340+ let mut properties = Vec :: new ( ) ;
1341+ for row in & mut result {
1342+ let Some ( name) = row. first ( ) . map ( value_to_string) else {
1343+ continue ;
1344+ } ;
1345+ let property_type = row
1346+ . get ( 1 )
1347+ . map ( value_to_string)
1348+ . unwrap_or_else ( || "UNKNOWN" . to_string ( ) ) ;
1349+ let is_primary_key = row. iter ( ) . any ( |value| matches ! ( value, Value :: Bool ( true ) ) ) ;
1350+ properties. push ( ( name, property_type, is_primary_key) ) ;
1351+ }
1352+ properties
1353+ }
1354+
1355+ fn schema_node_properties (
1356+ conn : & Connection ,
1357+ table_name : & str ,
1358+ include_primary_key : bool ,
1359+ ) -> HashMap < String , String > {
1360+ table_properties ( conn, table_name)
1361+ . into_iter ( )
1362+ . map ( |( name, property_type, is_primary_key) | {
1363+ let value = if include_primary_key && is_primary_key {
1364+ format ! ( "{property_type} primary key" )
1365+ } else {
1366+ property_type
1367+ } ;
1368+ ( name, value)
1369+ } )
1370+ . collect ( )
1371+ }
1372+
1373+ fn collect_schema_graph ( conn : & Connection ) -> Result < GraphData , String > {
1374+ let mut tables = conn
1375+ . query ( "CALL show_tables() RETURN *;" )
1376+ . map_err ( |e| format ! ( "Schema table query failed: {e}" ) ) ?;
1377+
1378+ let mut node_tables = Vec :: new ( ) ;
1379+ let mut rel_tables = Vec :: new ( ) ;
1380+ for row in & mut tables {
1381+ if row. len ( ) < 2 {
1382+ continue ;
1383+ }
1384+ let table_name = value_to_string ( & row[ 0 ] ) ;
1385+ let table_type = value_to_string ( & row[ 1 ] ) ;
1386+ if table_type. eq_ignore_ascii_case ( "NODE" ) {
1387+ node_tables. push ( table_name) ;
1388+ } else if table_type. eq_ignore_ascii_case ( "REL" ) {
1389+ rel_tables. push ( table_name) ;
1390+ }
1391+ }
1392+
1393+ node_tables. sort ( ) ;
1394+ rel_tables. sort ( ) ;
1395+
1396+ let nodes: Vec < GraphNode > = node_tables
1397+ . iter ( )
1398+ . map ( |table_name| GraphNode {
1399+ id : table_name. clone ( ) ,
1400+ name : table_name. clone ( ) ,
1401+ label : "Node Table" . to_string ( ) ,
1402+ properties : schema_node_properties ( conn, table_name, true ) ,
1403+ table_id : None ,
1404+ rowid : None ,
1405+ community : None ,
1406+ expansion_kind : None ,
1407+ expand_node_id : None ,
1408+ offset : None ,
1409+ hidden_count : None ,
1410+ color_key : Some ( format ! ( "schema-node:{table_name}" ) ) ,
1411+ } )
1412+ . collect ( ) ;
1413+
1414+ let node_table_set: HashSet < & str > = node_tables. iter ( ) . map ( String :: as_str) . collect ( ) ;
1415+ let mut links = Vec :: new ( ) ;
1416+ let mut seen_links = HashSet :: new ( ) ;
1417+
1418+ for rel_table in & rel_tables {
1419+ let Ok ( mut result) = conn. query ( & format ! (
1420+ "CALL SHOW_CONNECTION('{}') RETURN *;" ,
1421+ escaped_identifier( rel_table)
1422+ ) ) else {
1423+ continue ;
1424+ } ;
1425+
1426+ for row in & mut result {
1427+ if row. len ( ) < 2 {
1428+ continue ;
1429+ }
1430+ let source = value_to_string ( & row[ 0 ] ) ;
1431+ let target = value_to_string ( & row[ 1 ] ) ;
1432+ if !node_table_set. contains ( source. as_str ( ) )
1433+ || !node_table_set. contains ( target. as_str ( ) )
1434+ {
1435+ continue ;
1436+ }
1437+ merge_link (
1438+ & mut links,
1439+ & mut seen_links,
1440+ GraphLink {
1441+ source,
1442+ target,
1443+ label : rel_table. clone ( ) ,
1444+ } ,
1445+ ) ;
1446+ }
1447+ }
1448+
1449+ Ok ( graph_data_without_clusters ( nodes, links, "collect_schema_graph" ) )
1450+ }
1451+
13221452fn add_expanders (
13231453 graph : & GraphData ,
13241454 visible_ids : & HashSet < String > ,
@@ -1678,6 +1808,18 @@ fn get_graph(
16781808 . map ( |full_graph| seed_graph_from_full ( full_graph, llm_config. as_ref ( ) ) )
16791809}
16801810
1811+ #[ tauri:: command]
1812+ fn get_schema_graph ( state : State < AppState > , id : usize ) -> Result < GraphData , String > {
1813+ let databases = get_all_databases ( & state) ;
1814+ let db_info = databases. get ( id) . ok_or ( "Database not found" ) ?;
1815+
1816+ let db = Database :: new ( & db_info. path , SystemConfig :: default ( ) )
1817+ . map_err ( |e| format ! ( "Failed to open database: {}" , e) ) ?;
1818+ let conn = Connection :: new ( & db) . map_err ( |e| format ! ( "Failed to create connection: {}" , e) ) ?;
1819+
1820+ collect_schema_graph ( & conn)
1821+ }
1822+
16811823#[ tauri:: command]
16821824fn search_nodes (
16831825 state : State < AppState > ,
@@ -1885,6 +2027,7 @@ fn execute_query(
18852027 expand_node_id : None ,
18862028 offset : None ,
18872029 hidden_count : None ,
2030+ color_key : None ,
18882031 } ) ;
18892032 }
18902033 }
@@ -1952,6 +2095,7 @@ pub fn run() {
19522095 add_database,
19532096 get_directories,
19542097 get_graph,
2098+ get_schema_graph,
19552099 search_nodes,
19562100 get_node_neighborhood,
19572101 expand_node,
0 commit comments