42
42
import org .neo4j .cypherdsl .core .Node ;
43
43
import org .neo4j .cypherdsl .core .Parameter ;
44
44
import org .neo4j .cypherdsl .core .Relationship ;
45
+ import org .neo4j .cypherdsl .core .RelationshipPattern ;
45
46
import org .neo4j .cypherdsl .core .Statement ;
46
47
import org .neo4j .cypherdsl .core .StatementBuilder ;
47
48
import org .neo4j .cypherdsl .core .StatementBuilder .OngoingMatchAndUpdate ;
48
49
import org .neo4j .cypherdsl .core .SymbolicName ;
49
50
import org .springframework .data .mapping .MappingException ;
50
51
import org .springframework .data .mapping .PersistentProperty ;
52
+ import org .springframework .data .neo4j .core .schema .Relationship .Direction ;
51
53
import org .springframework .lang .NonNull ;
52
54
import org .springframework .lang .Nullable ;
53
55
import org .springframework .util .Assert ;
@@ -329,10 +331,8 @@ public Expression createReturnStatementForMatch(NodeDescription<?> nodeDescripti
329
331
330
332
SymbolicName nodeName = Constants .NAME_OF_ROOT_NODE ;
331
333
List <RelationshipDescription > processedRelationships = new ArrayList <>();
332
- boolean containsPossibleCircles = containsPossibleCircles (nodeDescription );
333
334
334
- return projectPropertiesAndRelationships (nodeDescription , nodeName , includeField ,
335
- processedRelationships , containsPossibleCircles );
335
+ return projectPropertiesAndRelationships (nodeDescription , nodeName , includeField , processedRelationships );
336
336
}
337
337
338
338
// recursive entry point for relationships in return statement
@@ -341,19 +341,17 @@ private MapProjection projectAllPropertiesAndRelationships(NodeDescription<?> no
341
341
342
342
Predicate <String > includeAllFields = (field ) -> true ;
343
343
// Because we are getting called recursive, there cannot be any circle
344
- return projectPropertiesAndRelationships (nodeDescription , nodeName , includeAllFields , processedRelationships ,
345
- false );
344
+ return projectPropertiesAndRelationships (nodeDescription , nodeName , includeAllFields , processedRelationships );
346
345
}
347
346
348
347
private MapProjection projectPropertiesAndRelationships (NodeDescription <?> nodeDescription , SymbolicName nodeName ,
349
- Predicate <String > includeProperty , List <RelationshipDescription > processedRelationships ,
350
- boolean containsPossibleCircles ) {
348
+ Predicate <String > includeProperty , List <RelationshipDescription > processedRelationships ) {
351
349
352
350
List <Object > propertiesProjection = projectNodeProperties (nodeDescription , nodeName , includeProperty );
353
351
List <Object > contentOfProjection = new ArrayList <>(propertiesProjection );
354
- if (containsPossibleCircles ) {
355
- Relationship pattern = anyNode (nodeName ). relationshipBetween ( anyNode (),
356
- collectAllRelationshipTypes ( nodeDescription )). unbounded ( );
352
+ if (nodeDescription . containsPossibleCircles () ) {
353
+ Node node = anyNode (nodeName );
354
+ RelationshipPattern pattern = createRelationships ( node , nodeDescription . getRelationships () );
357
355
NamedPath p = Cypher .path ("p" ).definedBy (pattern );
358
356
contentOfProjection .add (Constants .NAME_OF_PATHS );
359
357
contentOfProjection .add (Cypher .listBasedOn (p ).returning (p ));
@@ -364,41 +362,116 @@ private MapProjection projectPropertiesAndRelationships(NodeDescription<?> nodeD
364
362
return Cypher .anyNode (nodeName ).project (contentOfProjection );
365
363
}
366
364
367
- private boolean containsPossibleCircles ( NodeDescription <?> nodeDescription ) {
368
- Collection < RelationshipDescription > relationships = nodeDescription . getRelationships () ;
365
+ private RelationshipPattern createRelationships ( Node node , Collection < RelationshipDescription > relationshipDescriptions ) {
366
+ RelationshipPattern relationship ;
369
367
370
- Set <RelationshipDescription > processedRelationships = new HashSet <>();
371
- for (RelationshipDescription relationship : relationships ) {
372
- if (processedRelationships .contains (relationship )) {
373
- return true ;
368
+ Direction determinedDirection = determineDirection (relationshipDescriptions );
369
+ if (Direction .OUTGOING .equals (determinedDirection )) {
370
+ relationship = node .relationshipTo (anyNode (), collectFirstLevelRelationshipTypes (relationshipDescriptions ));
371
+ } else if (Direction .INCOMING .equals (determinedDirection )) {
372
+ relationship = node .relationshipFrom (anyNode (), collectFirstLevelRelationshipTypes (relationshipDescriptions ));
373
+ } else {
374
+ relationship = node .relationshipBetween (anyNode (), collectFirstLevelRelationshipTypes (relationshipDescriptions ));
375
+ }
376
+
377
+ Set <RelationshipDescription > processedRelationshipDescriptions = new HashSet <>(relationshipDescriptions );
378
+ for (RelationshipDescription relationshipDescription : relationshipDescriptions ) {
379
+ Collection <RelationshipDescription > relationships = relationshipDescription .getTarget ().getRelationships ();
380
+ if (relationships .size () > 0 ) {
381
+ relationship = createRelationships (relationship , relationships , processedRelationshipDescriptions )
382
+ .relationship ;
383
+ }
384
+ }
385
+
386
+ return relationship ;
387
+ }
388
+
389
+ private RelationshipProcessState createRelationships (RelationshipPattern existingRelationship ,
390
+ Collection <RelationshipDescription > relationshipDescriptions ,
391
+ Set <RelationshipDescription > processedRelationshipDescriptions ) {
392
+
393
+ RelationshipPattern relationship = existingRelationship ;
394
+ String [] relationshipTypes = collectAllRelationshipTypes (relationshipDescriptions );
395
+ if (processedRelationshipDescriptions .containsAll (relationshipDescriptions )) {
396
+ return new RelationshipProcessState (
397
+ relationship .relationshipBetween (anyNode (),
398
+ relationshipTypes ).unbounded ().min (0 ), true );
399
+ }
400
+ processedRelationshipDescriptions .addAll (relationshipDescriptions );
401
+
402
+ // we can process through the path
403
+ if (relationshipDescriptions .size () == 1 ) {
404
+ RelationshipDescription relationshipDescription = relationshipDescriptions .iterator ().next ();
405
+ switch (relationshipDescription .getDirection ()) {
406
+ case OUTGOING :
407
+ relationship = existingRelationship .relationshipTo (anyNode (),
408
+ collectFirstLevelRelationshipTypes (relationshipDescriptions )).unbounded ().min (0 ).max (1 );
409
+ break ;
410
+ case INCOMING :
411
+ relationship = existingRelationship .relationshipFrom (anyNode (),
412
+ collectFirstLevelRelationshipTypes (relationshipDescriptions )).unbounded ().min (0 ).max (1 );
413
+ break ;
414
+ default :
415
+ relationship = existingRelationship .relationshipBetween (anyNode (),
416
+ collectFirstLevelRelationshipTypes (relationshipDescriptions )).unbounded ().min (0 ).max (1 );
374
417
}
375
- processedRelationships .add (relationship );
376
- if (containsPossibleCircles (relationship .getTarget (), processedRelationships )) {
377
- return true ;
418
+
419
+ RelationshipProcessState relationships = createRelationships (relationship ,
420
+ relationshipDescription .getTarget ().getRelationships (), processedRelationshipDescriptions );
421
+
422
+ if (!relationships .done ) {
423
+ relationship = relationships .relationship ;
378
424
}
425
+ } else {
426
+ Direction determinedDirection = determineDirection (relationshipDescriptions );
427
+ if (Direction .OUTGOING .equals (determinedDirection )) {
428
+ relationship = existingRelationship .relationshipTo (anyNode (), relationshipTypes ).unbounded ().min (0 );
429
+ } else if (Direction .INCOMING .equals (determinedDirection )) {
430
+ relationship = existingRelationship .relationshipFrom (anyNode (), relationshipTypes ).unbounded ().min (0 );
431
+ } else {
432
+ relationship = existingRelationship .relationshipBetween (anyNode (), relationshipTypes ).unbounded ().min (0 );
433
+ }
434
+ return new RelationshipProcessState (relationship , true );
379
435
}
380
- return false ;
436
+ return new RelationshipProcessState ( relationship , false ) ;
381
437
}
382
438
383
- private boolean containsPossibleCircles ( NodeDescription <?> nodeDescription , Set < RelationshipDescription > processedRelationships ) {
384
- Collection <RelationshipDescription > relationships = nodeDescription . getRelationships ();
439
+ @ Nullable
440
+ Direction determineDirection ( Collection <RelationshipDescription > relationshipDescriptions ) {
385
441
386
- for (RelationshipDescription relationship : relationships ) {
387
- if (processedRelationships .contains (relationship )) {
388
- return true ;
442
+ Direction direction = null ;
443
+ for (RelationshipDescription relationshipDescription : relationshipDescriptions ) {
444
+ if (direction == null ) {
445
+ direction = relationshipDescription .getDirection ();
389
446
}
390
- processedRelationships .add (relationship );
391
- if (containsPossibleCircles (relationship .getTarget (), processedRelationships )) {
392
- return true ;
447
+ if (!direction .equals (relationshipDescription .getDirection ())) {
448
+ return null ;
393
449
}
394
450
}
395
- return false ;
451
+ return direction ;
396
452
}
397
453
398
- private String [] collectAllRelationshipTypes ( NodeDescription <?> nodeDescription ) {
454
+ private String [] collectFirstLevelRelationshipTypes ( Collection < RelationshipDescription > relationshipDescriptions ) {
399
455
Set <String > relationshipTypes = new HashSet <>();
400
456
401
- for (RelationshipDescription relationshipDescription : nodeDescription .getRelationships ()) {
457
+ for (RelationshipDescription relationshipDescription : relationshipDescriptions ) {
458
+ String relationshipType = relationshipDescription .getType ();
459
+ if (relationshipTypes .contains (relationshipType )) {
460
+ continue ;
461
+ }
462
+ if (relationshipDescription .isDynamic ()) {
463
+ relationshipTypes .clear ();
464
+ continue ;
465
+ }
466
+ relationshipTypes .add (relationshipType );
467
+ }
468
+ return relationshipTypes .toArray (new String [0 ]);
469
+ }
470
+
471
+ private String [] collectAllRelationshipTypes (Collection <RelationshipDescription > relationshipDescriptions ) {
472
+ Set <String > relationshipTypes = new HashSet <>();
473
+
474
+ for (RelationshipDescription relationshipDescription : relationshipDescriptions ) {
402
475
String relationshipType = relationshipDescription .getType ();
403
476
if (relationshipTypes .contains (relationshipType )) {
404
477
continue ;
@@ -421,6 +494,7 @@ private void collectAllRelationshipTypes(NodeDescription<?> nodeDescription, Set
421
494
continue ;
422
495
}
423
496
processedRelationshipTypes .add (relationshipType );
497
+ collectAllRelationshipTypes (relationshipDescription .getTarget (), processedRelationshipTypes );
424
498
}
425
499
}
426
500
@@ -546,4 +620,15 @@ private void addMapProjection(String name, Object projection, List<Object> proje
546
620
private static Condition conditionOrNoCondition (@ Nullable Condition condition ) {
547
621
return condition == null ? Conditions .noCondition () : condition ;
548
622
}
623
+
624
+ private static class RelationshipProcessState {
625
+ private final RelationshipPattern relationship ;
626
+ private final boolean done ;
627
+
628
+ RelationshipProcessState (RelationshipPattern relationship , boolean done ) {
629
+ this .relationship = relationship ;
630
+ this .done = done ;
631
+ }
632
+ }
633
+
549
634
}
0 commit comments