Skip to content

Commit 52535f3

Browse files
committed
GH-2801 - Fix false positive mapping cycle detection.
Closes #2801
1 parent 5e5cb23 commit 52535f3

File tree

2 files changed

+19
-10
lines changed

2 files changed

+19
-10
lines changed

src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jPersistentEntity.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -570,9 +570,10 @@ public boolean containsPossibleCircles(Predicate<PropertyFilter.RelaxedPropertyP
570570
}
571571

572572
private boolean calculatePossibleCircles(Predicate<PropertyFilter.RelaxedPropertyPath> includeField) {
573-
Collection<RelationshipDescription> relationships = new HashSet<>(getRelationshipsInHierarchy(includeField));
573+
Collection<RelationshipDescription> allRelationships = new HashSet<>(getRelationshipsInHierarchy(includeField));
574574

575-
for (RelationshipDescription relationship : relationships) {
575+
Set<NodeDescription<?>> thisNodeVisited = Set.of(this);
576+
for (RelationshipDescription relationship : allRelationships) {
576577
PropertyFilter.RelaxedPropertyPath relaxedPropertyPath = PropertyFilter.RelaxedPropertyPath.withRootType(this.getUnderlyingClass());
577578
if (!filterProperties(includeField, relationship, relaxedPropertyPath)) {
578579
continue;
@@ -583,24 +584,28 @@ private boolean calculatePossibleCircles(Predicate<PropertyFilter.RelaxedPropert
583584
if (this.equals(targetNode)) {
584585
return true;
585586
}
586-
String relationshipPropertiesPrefix = relationship.hasRelationshipProperties() ? "." + ((Neo4jPersistentEntity<?>) relationship.getRelationshipPropertiesEntity())
587-
.getPersistentProperty(TargetNode.class).getFieldName() : "";
588587

589588
// Branch out with the nodes already visited before
590-
Set<NodeDescription<?>> visitedNodes = new HashSet<>();
589+
Set<NodeDescription<?>> visitedNodes = new HashSet<>(thisNodeVisited);
591590
visitedNodes.add(targetNode);
592-
if (calculatePossibleCircles(targetNode, visitedNodes, includeField, relaxedPropertyPath.append(relationship.getFieldName() + relationshipPropertiesPrefix))) {
591+
592+
// we don't care about the other content of relationship properties and jump straight into the `TargetNode`
593+
String relationshipPropertiesPrefix = relationship.hasRelationshipProperties()
594+
? "." + ((Neo4jPersistentEntity<?>) relationship.getRelationshipPropertiesEntity()).getPersistentProperty(TargetNode.class).getFieldName()
595+
: "";
596+
PropertyFilter.RelaxedPropertyPath nextPath = relaxedPropertyPath.append(relationship.getFieldName() + relationshipPropertiesPrefix);
597+
if (calculatePossibleCircles(targetNode, visitedNodes, includeField, nextPath)) {
593598
return true;
594599
}
595600
}
596601
return false;
597602
}
598603

599604
private boolean calculatePossibleCircles(NodeDescription<?> nodeDescription, Set<NodeDescription<?>> visitedNodes, Predicate<PropertyFilter.RelaxedPropertyPath> includeField, PropertyFilter.RelaxedPropertyPath path) {
600-
Collection<RelationshipDescription> relationships = ((DefaultNeo4jPersistentEntity<?>) nodeDescription).getRelationshipsInHierarchy(includeField, path);
605+
Collection<RelationshipDescription> allRelationships = new HashSet<>(((DefaultNeo4jPersistentEntity<?>) nodeDescription).getRelationshipsInHierarchy(includeField, path));
601606

602607
Collection<NodeDescription<?>> visitedTargetNodes = new HashSet<>();
603-
for (RelationshipDescription relationship : relationships) {
608+
for (RelationshipDescription relationship : allRelationships) {
604609
NodeDescription<?> targetNode = relationship.getTarget();
605610
if (visitedNodes.contains(targetNode)) {
606611
return true;
@@ -611,8 +616,10 @@ private boolean calculatePossibleCircles(NodeDescription<?> nodeDescription, Set
611616
// Add the already visited target nodes for the next level,
612617
// but don't (!) add them to the visitedNodes yet.
613618
// Otherwise, the same "parallel" defined target nodes will report a false circle.
614-
branchedVisitedNodes.addAll(visitedTargetNodes);
615-
if (calculatePossibleCircles(targetNode, branchedVisitedNodes, includeField, path.append(relationship.getFieldName()))) {
619+
branchedVisitedNodes.add(targetNode);
620+
String relationshipPropertiesPrefix = relationship.hasRelationshipProperties() ? "." + ((Neo4jPersistentEntity<?>) relationship.getRelationshipPropertiesEntity())
621+
.getPersistentProperty(TargetNode.class).getFieldName() : "";
622+
if (calculatePossibleCircles(targetNode, branchedVisitedNodes, includeField, path.append(relationship.getFieldName() + relationshipPropertiesPrefix))) {
616623
return true;
617624
}
618625
}

src/main/java/org/springframework/data/neo4j/core/mapping/RelationshipDescription.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public interface RelationshipDescription {
6060

6161
/**
6262
* The target of this relationship is described by the primary label of the node in question.
63+
* If the relationship description includes a relationship properties class, this will be the {@link NodeDescription}
64+
* of the {@link org.springframework.data.neo4j.core.schema.TargetNode}.
6365
*
6466
* @return The target of this relationship
6567
*/

0 commit comments

Comments
 (0)