@@ -263,6 +263,8 @@ pub struct Graph {
263263 cache : Arc < Mutex < LruCache < String , PlanTree > > > ,
264264 /// Version counter (incremented on each write transaction)
265265 pub version : u64 ,
266+ /// Schema version (incremented only on schema changes: new labels, relationship types, or attributes)
267+ pub schema_version : u64 ,
266268}
267269
268270/// Wrapper for plan trees to implement Send+Sync.
@@ -440,6 +442,7 @@ impl Graph {
440442 NonZeroUsize :: new ( cache_size. max ( 1 ) ) . expect ( "cache_size.max(1) is always >= 1" ) ,
441443 ) ) ) ,
442444 version,
445+ schema_version : 0 ,
443446 }
444447 }
445448
@@ -476,6 +479,7 @@ impl Graph {
476479 relationship_types : self . relationship_types . clone ( ) ,
477480 cache : self . cache . clone ( ) ,
478481 version : self . version + 1 ,
482+ schema_version : self . schema_version ,
479483 }
480484 }
481485
@@ -611,6 +615,29 @@ impl Graph {
611615 . map ( TypeId )
612616 }
613617
618+ /// Get-or-create a relationship type by name, returning its `TypeId`.
619+ pub fn get_type_id_mut (
620+ & mut self ,
621+ relationship_type : & str ,
622+ ) -> TypeId {
623+ if let Some ( pos) = self
624+ . relationship_types
625+ . iter ( )
626+ . position ( |t| t. as_str ( ) == relationship_type)
627+ . map ( TypeId )
628+ {
629+ return pos;
630+ }
631+
632+ self . relationship_types
633+ . push ( Arc :: new ( relationship_type. to_string ( ) ) ) ;
634+ self . relationship_matrices . insert (
635+ self . relationship_types . len ( ) - 1 ,
636+ Tensor :: new ( self . node_cap , self . node_cap ) ,
637+ ) ;
638+ TypeId ( self . relationship_types . len ( ) - 1 )
639+ }
640+
614641 pub fn get_plan (
615642 & self ,
616643 query : & str ,
@@ -782,6 +809,10 @@ impl Graph {
782809 NodeId ( self . node_count + self . reserved_node_count - 1 )
783810 }
784811
812+ pub const fn inc_reserved_node_count ( & mut self ) {
813+ self . reserved_node_count += 1 ;
814+ }
815+
785816 pub fn reserve_nodes (
786817 & mut self ,
787818 count : usize ,
@@ -832,6 +863,14 @@ impl Graph {
832863 self . node_count + self . deleted_nodes . len ( ) - 1
833864 }
834865
866+ #[ must_use]
867+ pub fn max_relationship_id ( & self ) -> u64 {
868+ if self . relationship_count == 0 {
869+ return 0 ;
870+ }
871+ self . relationship_count + self . deleted_relationships . len ( ) - 1
872+ }
873+
835874 pub fn set_nodes_attributes (
836875 & mut self ,
837876 attrs : & HashMap < u64 , OrderMap < Arc < String > , Value > > ,
@@ -1095,6 +1134,10 @@ impl Graph {
10951134 self . node_attrs . get_attr_by_idx ( id. 0 , attr_idx)
10961135 }
10971136
1137+ pub const fn inc_reserved_relationship_count ( & mut self ) {
1138+ self . reserved_relationship_count += 1 ;
1139+ }
1140+
10981141 pub fn reserve_relationship ( & mut self ) -> RelationshipId {
10991142 if self . reserved_relationship_count < self . deleted_relationships . len ( ) {
11001143 let id = self
@@ -1402,21 +1445,30 @@ impl Graph {
14021445 self . relationship_attrs . get_attr ( id. 0 , attr)
14031446 }
14041447
1448+ fn resize_node_matrices ( & mut self ) {
1449+ self . adjacancy_matrix . resize ( self . node_cap , self . node_cap ) ;
1450+ self . node_labels_matrix
1451+ . resize ( self . node_cap , self . labels_matices . len ( ) as u64 ) ;
1452+ self . all_nodes_matrix . resize ( self . node_cap , self . node_cap ) ;
1453+ for label_matrix in & mut self . labels_matices {
1454+ label_matrix. resize ( self . node_cap , self . node_cap ) ;
1455+ }
1456+ for relationship_matrix in & mut self . relationship_matrices {
1457+ relationship_matrix. resize ( self . node_cap , self . node_cap ) ;
1458+ }
1459+ }
1460+
1461+ fn resize_relationship_matrices ( & mut self ) {
1462+ self . relationship_type_matrix
1463+ . resize ( self . relationship_cap , self . relationship_types . len ( ) as u64 ) ;
1464+ }
1465+
14051466 fn resize ( & mut self ) {
14061467 if self . node_count > self . node_cap {
14071468 while self . node_count > self . node_cap {
14081469 self . node_cap *= 2 ;
14091470 }
1410- self . adjacancy_matrix . resize ( self . node_cap , self . node_cap ) ;
1411- self . node_labels_matrix
1412- . resize ( self . node_cap , self . labels_matices . len ( ) as u64 ) ;
1413- self . all_nodes_matrix . resize ( self . node_cap , self . node_cap ) ;
1414- for label_matrix in & mut self . labels_matices {
1415- label_matrix. resize ( self . node_cap , self . node_cap ) ;
1416- }
1417- for relationship_matrix in & mut self . relationship_matrices {
1418- relationship_matrix. resize ( self . node_cap , self . node_cap ) ;
1419- }
1471+ self . resize_node_matrices ( ) ;
14201472 }
14211473
14221474 if self . labels_matices . len ( ) as u64 > self . node_labels_matrix . ncols ( ) {
@@ -1428,8 +1480,7 @@ impl Graph {
14281480 while self . relationship_count > self . relationship_cap {
14291481 self . relationship_cap *= 2 ;
14301482 }
1431- self . relationship_type_matrix
1432- . resize ( self . relationship_cap , self . relationship_types . len ( ) as u64 ) ;
1483+ self . resize_relationship_matrices ( ) ;
14331484 }
14341485
14351486 if self . relationship_types . len ( ) as u64 > self . relationship_type_matrix . ncols ( ) {
@@ -1877,4 +1928,144 @@ impl Graph {
18771928 }
18781929 sz
18791930 }
1931+
1932+ #[ must_use]
1933+ pub fn deleted_nodes_count ( & self ) -> u64 {
1934+ self . deleted_nodes . len ( )
1935+ }
1936+
1937+ #[ must_use]
1938+ pub fn deleted_relationships_count ( & self ) -> u64 {
1939+ self . deleted_relationships . len ( )
1940+ }
1941+
1942+ #[ must_use]
1943+ pub const fn deleted_relationships ( & self ) -> & RoaringTreemap {
1944+ & self . deleted_relationships
1945+ }
1946+
1947+ #[ must_use]
1948+ pub fn label_matrices ( & self ) -> & [ VersionedMatrix ] {
1949+ & self . labels_matices
1950+ }
1951+
1952+ #[ must_use]
1953+ pub fn relationship_tensors ( & self ) -> & [ Tensor ] {
1954+ & self . relationship_matrices
1955+ }
1956+
1957+ /// Synchronously create an index (without spawning background population).
1958+ pub fn create_index_sync (
1959+ & mut self ,
1960+ index_type : & IndexType ,
1961+ entity_type : & EntityType ,
1962+ label : & Arc < String > ,
1963+ attrs : & Vec < Arc < String > > ,
1964+ options : Option < IndexOptions > ,
1965+ ) -> Result < ( ) , String > {
1966+ match entity_type {
1967+ EntityType :: Node => {
1968+ let len = self . get_label_matrix_mut ( label) . nvals ( ) ;
1969+ self . node_indexer
1970+ . create_index ( index_type, label, attrs, len, options) ?;
1971+ }
1972+ EntityType :: Relationship => { }
1973+ }
1974+ Ok ( ( ) )
1975+ }
1976+
1977+ /// Synchronously populate all pending indexes.
1978+ /// Used after RDB load when the graph is fully constructed.
1979+ pub fn populate_indexes_sync ( & mut self ) {
1980+ let fields_by_label = self . node_indexer . get_all_pending_fields ( ) ;
1981+ for ( label, attrs) in fields_by_label {
1982+ if let Some ( lm) = self . get_label_matrix ( & label) {
1983+ let resolved_attrs: Vec < ( u16 , Vec < _ > ) > = attrs
1984+ . iter ( )
1985+ . filter_map ( |( attr, fields) | {
1986+ self . get_node_attribute_id ( attr)
1987+ . map ( |idx| ( idx as u16 , fields. clone ( ) ) )
1988+ } )
1989+ . collect ( ) ;
1990+
1991+ let mut batch = Vec :: new ( ) ;
1992+ for ( n, _) in lm. iter ( 0 , u64:: MAX ) {
1993+ let mut doc = Document :: new ( n) ;
1994+ let mut has_fields = false ;
1995+ for ( attr_idx, fields) in & resolved_attrs {
1996+ let value = self . get_node_attribute_by_idx ( NodeId ( n) , * attr_idx) ;
1997+ if let Some ( value) = value {
1998+ for field in fields {
1999+ doc. set ( field, & value) ;
2000+ }
2001+ has_fields = true ;
2002+ }
2003+ }
2004+ if has_fields {
2005+ batch. push ( doc) ;
2006+ }
2007+ }
2008+ if !batch. is_empty ( ) {
2009+ let mut add_docs = HashMap :: new ( ) ;
2010+ add_docs. insert ( label. clone ( ) , batch) ;
2011+ self . node_indexer . commit ( & mut add_docs, & mut HashMap :: new ( ) ) ;
2012+ }
2013+ self . node_indexer . enable ( & label) ;
2014+ }
2015+ }
2016+ }
2017+
2018+ /// Get node attribute names.
2019+ pub fn get_node_attribute_names ( & self ) -> Vec < Arc < String > > {
2020+ self . node_attrs . attrs_name . iter ( ) . cloned ( ) . collect ( )
2021+ }
2022+
2023+ /// Get relationship attribute names.
2024+ pub fn get_relationship_attribute_names ( & self ) -> Vec < Arc < String > > {
2025+ self . relationship_attrs . attrs_name . iter ( ) . cloned ( ) . collect ( )
2026+ }
2027+
2028+ /// Register a node attribute name (get-or-create).
2029+ pub fn add_node_attribute_name (
2030+ & mut self ,
2031+ name : & str ,
2032+ ) {
2033+ let arc = Arc :: new ( name. to_string ( ) ) ;
2034+ if self . node_attrs . attrs_name . get_index_of ( & arc) . is_none ( ) {
2035+ self . node_attrs . attrs_name . insert ( arc) ;
2036+ }
2037+ }
2038+
2039+ /// Register a relationship attribute name (get-or-create).
2040+ pub fn add_rel_attribute_name (
2041+ & mut self ,
2042+ name : & str ,
2043+ ) {
2044+ let arc = Arc :: new ( name. to_string ( ) ) ;
2045+ if self
2046+ . relationship_attrs
2047+ . attrs_name
2048+ . get_index_of ( & arc)
2049+ . is_none ( )
2050+ {
2051+ self . relationship_attrs . attrs_name . insert ( arc) ;
2052+ }
2053+ }
2054+
2055+ /// Build the unified global attribute list (node attrs ∪ relationship attrs, in order).
2056+ pub fn build_global_attrs ( & self ) -> Vec < Arc < String > > {
2057+ let mut attrs = Vec :: new ( ) ;
2058+ let mut seen = std:: collections:: HashSet :: new ( ) ;
2059+ for name in self . node_attrs . attrs_name . iter ( ) {
2060+ if seen. insert ( name. clone ( ) ) {
2061+ attrs. push ( name. clone ( ) ) ;
2062+ }
2063+ }
2064+ for name in self . relationship_attrs . attrs_name . iter ( ) {
2065+ if seen. insert ( name. clone ( ) ) {
2066+ attrs. push ( name. clone ( ) ) ;
2067+ }
2068+ }
2069+ attrs
2070+ }
18802071}
0 commit comments