@@ -326,6 +326,7 @@ const SRC_PROPS_PARAM: &str = "source_props";
326
326
const TGT_KEY_PARAM_PREFIX : & str = "target_key" ;
327
327
const TGT_PROPS_PARAM : & str = "target_props" ;
328
328
const CORE_ELEMENT_MATCHER_VAR : & str = "e" ;
329
+ const SELF_CONTAINED_TAG_FIELD_NAME : & str = "__self_contained" ;
329
330
330
331
impl ExportContext {
331
332
fn build_key_field_params_n_literal < ' a > (
@@ -360,6 +361,8 @@ impl ExportContext {
360
361
let delete_cypher = formatdoc ! { "
361
362
OPTIONAL MATCH (old_node:{label} {key_fields_literal})
362
363
WITH old_node
364
+ SET old_node.{SELF_CONTAINED_TAG_FIELD_NAME} = NULL
365
+ WITH old_node
363
366
WHERE NOT (old_node)--()
364
367
DELETE old_node
365
368
FINISH
@@ -369,14 +372,14 @@ impl ExportContext {
369
372
370
373
let insert_cypher = formatdoc ! { "
371
374
MERGE (new_node:{label} {key_fields_literal})
372
- {optional_set_props}
375
+ SET new_node.{SELF_CONTAINED_TAG_FIELD_NAME} = TRUE {optional_set_props}
373
376
FINISH
374
377
" ,
375
378
label = node_spec. label,
376
379
optional_set_props = if value_fields. is_empty( ) {
377
380
"" . to_string( )
378
381
} else {
379
- format!( "SET new_node += ${CORE_PROPS_PARAM}\n " )
382
+ format!( ", new_node += ${CORE_PROPS_PARAM}\n " )
380
383
} ,
381
384
} ;
382
385
@@ -402,24 +405,12 @@ impl ExportContext {
402
405
403
406
DELETE old_rel
404
407
405
- WITH old_src, old_tgt
406
- CALL {{
407
- WITH old_src
408
- OPTIONAL MATCH (old_src)-[r]-()
409
- WITH old_src, count(r) AS rels
410
- WHERE rels = 0
411
- DELETE old_src
412
- RETURN 0 AS _1
413
- }}
414
-
415
- CALL {{
416
- WITH old_tgt
417
- OPTIONAL MATCH (old_tgt)-[r]-()
418
- WITH old_tgt, count(r) AS rels
419
- WHERE rels = 0
420
- DELETE old_tgt
421
- RETURN 0 AS _2
422
- }}
408
+ WITH collect(old_src) + collect(old_tgt) AS nodes_to_check
409
+ UNWIND nodes_to_check AS node
410
+ WITH DISTINCT node
411
+ WHERE NOT COALESCE(node.{SELF_CONTAINED_TAG_FIELD_NAME}, FALSE)
412
+ AND COUNT{{ (node)--() }} = 0
413
+ DELETE node
423
414
424
415
FINISH
425
416
" ,
@@ -478,7 +469,7 @@ impl ExportContext {
478
469
create_order : 1 ,
479
470
delete_cypher,
480
471
insert_cypher,
481
- delete_before_upsert : false , // true
472
+ delete_before_upsert : true ,
482
473
key_field_params,
483
474
key_fields,
484
475
value_fields,
@@ -936,20 +927,37 @@ impl ResourceSetupStatus for GraphElementDataSetupStatus {
936
927
async fn clear_graph_element_data (
937
928
graph : & Graph ,
938
929
key : & GraphElement ,
939
- is_dependent : bool ,
930
+ is_self_contained : bool ,
940
931
) -> Result < ( ) > {
941
932
let var_name = CORE_ELEMENT_MATCHER_VAR ;
942
- let optional_orphan_condition = if is_dependent {
943
- format ! ( "WHERE NOT ({var_name})--()" )
944
- } else {
945
- "" . to_string ( )
933
+ let matcher = key. typ . matcher ( var_name) ;
934
+ let query_string = match key. typ {
935
+ ElementType :: Node ( _) => {
936
+ let optional_reset_self_contained = if is_self_contained {
937
+ formatdoc ! { "
938
+ WITH {var_name}
939
+ SET {var_name}.{SELF_CONTAINED_TAG_FIELD_NAME} = NULL
940
+ " }
941
+ } else {
942
+ "" . to_string ( )
943
+ } ;
944
+ formatdoc ! { "
945
+ CALL {{
946
+ MATCH {matcher}
947
+ {optional_reset_self_contained}
948
+ WITH {var_name} WHERE NOT ({var_name})--() DELETE {var_name}
949
+ }} IN TRANSACTIONS
950
+ " }
951
+ }
952
+ ElementType :: Relationship ( _) => {
953
+ formatdoc ! { "
954
+ CALL {{
955
+ MATCH {matcher} WITH {var_name} DELETE {var_name}
956
+ }} IN TRANSACTIONS
957
+ " }
958
+ }
946
959
} ;
947
- let matcher = key. typ . matcher ( CORE_ELEMENT_MATCHER_VAR ) ;
948
- let delete_query = neo4rs:: query ( & formatdoc ! { "
949
- CALL {{
950
- MATCH {matcher} WITH {var_name} {optional_orphan_condition} DELETE {var_name}
951
- }} IN TRANSACTIONS
952
- " } ) ;
960
+ let delete_query = neo4rs:: query ( & query_string) ;
953
961
graph. run ( delete_query) . await ?;
954
962
Ok ( ( ) )
955
963
}
@@ -1348,18 +1356,18 @@ impl StorageFactoryBase for Factory {
1348
1356
// Relationships have no dependency, so can be cleared first.
1349
1357
for ( rel_type, conn_spec) in relationship_types. iter ( ) {
1350
1358
let graph = self . graph_pool . get_graph ( conn_spec) . await ?;
1351
- clear_graph_element_data ( & graph, rel_type, false ) . await ?;
1359
+ clear_graph_element_data ( & graph, rel_type, true ) . await ?;
1352
1360
}
1353
1361
// Clear standalone nodes, which is simpler than dependent nodes.
1354
1362
for ( node_label, conn_spec) in node_labels. iter ( ) {
1355
1363
let graph = self . graph_pool . get_graph ( conn_spec) . await ?;
1356
- clear_graph_element_data ( & graph, node_label, false ) . await ?;
1364
+ clear_graph_element_data ( & graph, node_label, true ) . await ?;
1357
1365
}
1358
1366
// Clear dependent nodes if they're not covered by standalone nodes.
1359
1367
for ( node_label, conn_spec) in dependent_node_labels. iter ( ) {
1360
1368
if !node_labels. contains_key ( node_label) {
1361
1369
let graph = self . graph_pool . get_graph ( conn_spec) . await ?;
1362
- clear_graph_element_data ( & graph, node_label, true ) . await ?;
1370
+ clear_graph_element_data ( & graph, node_label, false ) . await ?;
1363
1371
}
1364
1372
}
1365
1373
0 commit comments