@@ -531,13 +531,8 @@ fn transform_to_node(
531531 let id_value_refs: Vec < & str > = id_values. iter ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ;
532532 let element_id = generate_node_element_id ( & label, & id_value_refs) ;
533533
534- // Try to extract numeric ID for legacy `id` field
535- // For single-column numeric IDs, parse as i64; otherwise use 0
536- let legacy_id: i64 = if id_values. len ( ) == 1 {
537- id_values[ 0 ] . parse ( ) . unwrap_or ( 0 )
538- } else {
539- 0 // Composite IDs can't be represented as single i64
540- } ;
534+ // Derive integer ID from element_id (ensures uniqueness across labels)
535+ let id: i64 = generate_id_from_element_id ( & element_id) ;
541536
542537 // Create Node struct
543538 // Use inferred label if original labels was empty
@@ -548,7 +543,7 @@ fn transform_to_node(
548543 } ;
549544
550545 Ok ( Node {
551- id : legacy_id ,
546+ id,
552547 labels : final_labels,
553548 properties,
554549 element_id,
@@ -687,11 +682,16 @@ fn transform_to_relationship(
687682 let start_node_element_id = generate_node_element_id ( from_node_label, & from_id_refs) ;
688683 let end_node_element_id = generate_node_element_id ( to_node_label, & to_id_refs) ;
689684
685+ // Derive integer IDs from element_ids (ensures uniqueness across labels)
686+ let rel_id = generate_id_from_element_id ( & element_id) ;
687+ let start_node_id = generate_id_from_element_id ( & start_node_element_id) ;
688+ let end_node_id = generate_id_from_element_id ( & end_node_element_id) ;
689+
690690 // Create Relationship struct
691691 Ok ( Relationship {
692- id : 0 , // Legacy ID (unused in Neo4j 5.x)
693- start_node_id : 0 , // Legacy ID
694- end_node_id : 0 , // Legacy ID
692+ id : rel_id ,
693+ start_node_id,
694+ end_node_id,
695695 rel_type : rel_type. to_string ( ) ,
696696 properties,
697697 element_id,
@@ -885,9 +885,10 @@ fn transform_path_from_json(row: &HashMap<String, Value>) -> Result<Path, String
885885 }
886886 } ;
887887
888- // Create start node with explicit label and properties
889- let start_id = extract_id_from_props ( & start_props, "user_id" , "post_id" , "id" ) ;
890- let start_element_id = generate_node_element_id ( & start_label, & [ & start_id. to_string ( ) ] ) ;
888+ // Create start node - element_id is source of truth, integer id derived from it
889+ let start_prop_id = extract_id_from_props ( & start_props, "user_id" , "post_id" , "id" ) ;
890+ let start_element_id = generate_node_element_id ( & start_label, & [ & start_prop_id. to_string ( ) ] ) ;
891+ let start_id = generate_id_from_element_id ( & start_element_id) ;
891892 // Clean property keys (remove table alias prefix like "t1_0.")
892893 let start_props_clean = clean_property_keys ( start_props) ;
893894 let start_node = Node :: new (
@@ -897,9 +898,10 @@ fn transform_path_from_json(row: &HashMap<String, Value>) -> Result<Path, String
897898 start_element_id,
898899 ) ;
899900
900- // Create end node with explicit label and properties
901- let end_id = extract_id_from_props ( & end_props, "user_id" , "post_id" , "id" ) ;
902- let end_element_id = generate_node_element_id ( & end_label, & [ & end_id. to_string ( ) ] ) ;
901+ // Create end node - element_id is source of truth, integer id derived from it
902+ let end_prop_id = extract_id_from_props ( & end_props, "user_id" , "post_id" , "id" ) ;
903+ let end_element_id = generate_node_element_id ( & end_label, & [ & end_prop_id. to_string ( ) ] ) ;
904+ let end_id = generate_id_from_element_id ( & end_element_id) ;
903905 // Clean property keys
904906 let end_props_clean = clean_property_keys ( end_props) ;
905907 let end_node = Node :: new (
@@ -909,16 +911,18 @@ fn transform_path_from_json(row: &HashMap<String, Value>) -> Result<Path, String
909911 end_element_id,
910912 ) ;
911913
912- // Create relationship with type and properties
913- let rel_id = extract_id_from_props ( & rel_props, "from_id" , "follower_id" , "user_id" ) ;
914- let from_id_str = start_id. to_string ( ) ;
915- let to_id_str = end_id. to_string ( ) ;
916- let rel_element_id = generate_relationship_element_id ( & rel_type, & from_id_str, & to_id_str) ;
914+ // Create relationship - element_id is source of truth, integer id derived from it
915+ let rel_element_id = generate_relationship_element_id (
916+ & rel_type,
917+ & start_prop_id. to_string ( ) ,
918+ & end_prop_id. to_string ( ) ,
919+ ) ;
920+ let rel_id = generate_id_from_element_id ( & rel_element_id) ;
917921 let rel_props_clean = clean_property_keys ( rel_props) ;
918922 let relationship = Relationship :: new (
919923 rel_id,
920- start_id, // start_node_id
921- end_id, // end_node_id
924+ start_id, // start_node_id - derived from start_element_id
925+ end_id, // end_node_id - derived from end_element_id
922926 rel_type. clone ( ) ,
923927 rel_props_clean,
924928 rel_element_id,
@@ -1003,6 +1007,18 @@ fn extract_id_from_props(props: &HashMap<String, Value>, id1: &str, id2: &str, i
10031007 0
10041008}
10051009
1010+ /// Generate a unique integer node ID from element_id
1011+ /// This ensures round-trip: element_id is the source of truth, integer id is derived from it
1012+ fn generate_id_from_element_id ( element_id : & str ) -> i64 {
1013+ use std:: collections:: hash_map:: DefaultHasher ;
1014+ use std:: hash:: { Hash , Hasher } ;
1015+
1016+ let mut hasher = DefaultHasher :: new ( ) ;
1017+ element_id. hash ( & mut hasher) ;
1018+ // Use absolute value and mask to ensure positive i64
1019+ ( hasher. finish ( ) as i64 ) . abs ( )
1020+ }
1021+
10061022fn value_to_i64 ( val : & Value ) -> Option < i64 > {
10071023 match val {
10081024 Value :: Number ( n) => n. as_i64 ( ) ,
@@ -1082,7 +1098,8 @@ fn find_node_in_row_with_label(
10821098 label,
10831099 & id_values. iter ( ) . map ( |s| s. as_str ( ) ) . collect :: < Vec < _ > > ( ) ,
10841100 ) ;
1085- let id: i64 = id_values. first ( ) . and_then ( |s| s. parse ( ) . ok ( ) ) . unwrap_or ( 0 ) ;
1101+ // Derive integer ID from element_id (ensures uniqueness across labels)
1102+ let id: i64 = generate_id_from_element_id ( & element_id) ;
10861103
10871104 log:: info!(
10881105 "✅ Found node '{}' in row: label={}, properties={}, element_id={}" ,
@@ -1154,7 +1171,8 @@ fn find_node_in_row(
11541171
11551172 if !id_values. is_empty ( ) {
11561173 let element_id = format ! ( "{}:{}" , label, id_values. join( "|" ) ) ;
1157- let id: i64 = id_values. first ( ) . and_then ( |s| s. parse ( ) . ok ( ) ) . unwrap_or ( 0 ) ;
1174+ // Derive integer ID from element_id (ensures uniqueness across labels)
1175+ let id: i64 = generate_id_from_element_id ( & element_id) ;
11581176
11591177 return Some ( Node {
11601178 id,
@@ -1229,26 +1247,13 @@ fn find_relationship_in_row_with_type(
12291247 properties. len( )
12301248 ) ;
12311249
1232- // Extract node IDs from element_ids (format: "Label:id")
1233- let start_id = start_element_id
1234- . split ( ':' )
1235- . nth ( 1 )
1236- . and_then ( |s| s. parse :: < i64 > ( ) . ok ( ) )
1237- . unwrap_or ( 0 ) ;
1238- let end_id = end_element_id
1239- . split ( ':' )
1240- . nth ( 1 )
1241- . and_then ( |s| s. parse :: < i64 > ( ) . ok ( ) )
1242- . unwrap_or ( 0 ) ;
1243-
1244- // Generate unique relationship ID by combining start and end IDs
1245- // Use a simple hash: (start_id << 32) | end_id
1246- // This ensures each unique (from, to) pair gets a unique ID
1247- let rel_id = if start_id > 0 && end_id > 0 {
1248- ( ( start_id as i64 ) << 20 ) | ( end_id as i64 )
1249- } else {
1250- 0
1251- } ;
1250+ // Node IDs are derived from element_id hash (same as the nodes themselves)
1251+ let start_id = generate_id_from_element_id ( start_element_id) ;
1252+ let end_id = generate_id_from_element_id ( end_element_id) ;
1253+
1254+ // Generate relationship element_id from type and node element_ids
1255+ let rel_element_id = format ! ( "{}:{}->{}" , rel_type, start_element_id, end_element_id) ;
1256+ let rel_id = generate_id_from_element_id ( & rel_element_id) ;
12521257
12531258 // Create relationship with extracted properties
12541259 Some ( Relationship {
@@ -1257,7 +1262,7 @@ fn find_relationship_in_row_with_type(
12571262 end_node_id : end_id,
12581263 rel_type : rel_type. clone ( ) ,
12591264 properties,
1260- element_id : format ! ( "{}:{}->{}" , rel_type , start_id , end_id ) ,
1265+ element_id : rel_element_id ,
12611266 start_node_element_id : start_element_id. to_string ( ) ,
12621267 end_node_element_id : end_element_id. to_string ( ) ,
12631268 } )
@@ -1373,12 +1378,14 @@ fn create_placeholder_relationship(
13731378}
13741379
13751380/// Create a node with a known label but no data
1376- fn create_node_with_label ( label : & str , id : i64 ) -> Node {
1381+ fn create_node_with_label ( label : & str , idx : i64 ) -> Node {
1382+ let element_id = format ! ( "{}:{}" , label, idx) ;
1383+ let id = generate_id_from_element_id ( & element_id) ;
13771384 Node {
13781385 id,
13791386 labels : vec ! [ label. to_string( ) ] ,
13801387 properties : std:: collections:: HashMap :: new ( ) ,
1381- element_id : format ! ( "{}:{}" , label , id ) ,
1388+ element_id,
13821389 }
13831390}
13841391
@@ -1388,18 +1395,19 @@ fn create_relationship_with_type(
13881395 start_element_id : & str ,
13891396 end_element_id : & str ,
13901397) -> Relationship {
1391- // Extract just the ID portions from element IDs (format: "Label:id")
1392- let start_id = start_element_id. split ( ':' ) . last ( ) . unwrap_or ( "0" ) ;
1393- let end_id = end_element_id. split ( ':' ) . last ( ) . unwrap_or ( "0" ) ;
1398+ // Generate element_id and derive all integer IDs from element_ids
1399+ let element_id = format ! ( "{}:{}->{}" , rel_type, start_element_id, end_element_id) ;
1400+ let id = generate_id_from_element_id ( & element_id) ;
1401+ let start_node_id = generate_id_from_element_id ( start_element_id) ;
1402+ let end_node_id = generate_id_from_element_id ( end_element_id) ;
13941403
13951404 Relationship {
1396- id : 0 ,
1397- start_node_id : 0 ,
1398- end_node_id : 0 ,
1405+ id,
1406+ start_node_id,
1407+ end_node_id,
13991408 rel_type : rel_type. to_string ( ) ,
14001409 properties : std:: collections:: HashMap :: new ( ) ,
1401- // Use simpler elementId format: TYPE:from_id->to_id
1402- element_id : format ! ( "{}:{}->{}" , rel_type, start_id, end_id) ,
1410+ element_id,
14031411 start_node_element_id : start_element_id. to_string ( ) ,
14041412 end_node_element_id : end_element_id. to_string ( ) ,
14051413 }
0 commit comments