Skip to content

Commit b5cb295

Browse files
committed
Avoid wildcard for enum-based dynamic relationships.
Use the available enum values to create a more restrictive path query pattern.
1 parent b14db2a commit b5cb295

File tree

2 files changed

+91
-2
lines changed

2 files changed

+91
-2
lines changed

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static org.neo4j.cypherdsl.core.Cypher.parameter;
2424

2525
import java.util.ArrayList;
26+
import java.util.Arrays;
2627
import java.util.Collection;
2728
import java.util.HashSet;
2829
import java.util.List;
@@ -532,7 +533,7 @@ private String[] collectFirstLevelRelationshipTypes(Collection<RelationshipDescr
532533
continue;
533534
}
534535
if (relationshipDescription.isDynamic()) {
535-
relationshipTypes.clear();
536+
handleDynamicRelationship(relationshipTypes, (DefaultRelationshipDescription) relationshipDescription);
536537
continue;
537538
}
538539
relationshipTypes.add(relationshipType);
@@ -546,7 +547,7 @@ private String[] collectAllRelationshipTypes(Collection<RelationshipDescription>
546547
for (RelationshipDescription relationshipDescription : relationshipDescriptions) {
547548
String relationshipType = relationshipDescription.getType();
548549
if (relationshipDescription.isDynamic()) {
549-
relationshipTypes.clear();
550+
handleDynamicRelationship(relationshipTypes, (DefaultRelationshipDescription) relationshipDescription);
550551
continue;
551552
}
552553
relationshipTypes.add(relationshipType);
@@ -555,6 +556,16 @@ private String[] collectAllRelationshipTypes(Collection<RelationshipDescription>
555556
return relationshipTypes.toArray(new String[0]);
556557
}
557558

559+
private void handleDynamicRelationship(Set<String> relationshipTypes, DefaultRelationshipDescription relationshipDescription) {
560+
Class<?> componentType = relationshipDescription.getInverse().getComponentType();
561+
if (componentType != null && componentType.isEnum()) {
562+
Arrays.stream(componentType.getEnumConstants())
563+
.forEach(constantName -> relationshipTypes.add(constantName.toString()));
564+
} else {
565+
relationshipTypes.clear();
566+
}
567+
}
568+
558569
private void collectAllRelationshipTypes(NodeDescription<?> nodeDescription, Set<String> relationshipTypes,
559570
Collection<RelationshipDescription> processedRelationshipDescriptions) {
560571

src/test/java/org/springframework/data/neo4j/core/mapping/CypherGeneratorTest.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
import static org.mockito.Mockito.doReturn;
2121
import static org.mockito.Mockito.when;
2222

23+
import java.util.Collections;
2324
import java.util.Map;
2425
import java.util.Optional;
26+
import java.util.regex.Pattern;
2527
import java.util.stream.Stream;
2628

2729
import org.junit.Assert;
@@ -30,6 +32,7 @@
3032
import org.junit.jupiter.params.provider.Arguments;
3133
import org.junit.jupiter.params.provider.MethodSource;
3234
import org.mockito.Mockito;
35+
import org.neo4j.cypherdsl.core.Cypher;
3336
import org.neo4j.cypherdsl.core.Statement;
3437
import org.neo4j.cypherdsl.core.renderer.Renderer;
3538
import org.springframework.data.domain.Sort;
@@ -139,6 +142,44 @@ void shouldCreateRelationshipRemoveQueryWithoutUsingInternalIds() {
139142
Assert.assertEquals(expectedQuery, Renderer.getDefaultRenderer().render(statement));
140143
}
141144

145+
@Test
146+
void shouldCreateDynamicRelationshipPathQueryForEnumsWithoutWildcardRelationships() {
147+
Neo4jPersistentEntity<?> persistentEntity = new Neo4jMappingContext()
148+
.getPersistentEntity(CyclicEntityWithEnumeratedDynamicRelationship1.class);
149+
150+
org.neo4j.cypherdsl.core.Node rootNode = Cypher.anyNode(Constants.NAME_OF_ROOT_NODE);
151+
Statement statement = CypherGenerator.INSTANCE.createPathMatchWithCondition(null, persistentEntity,
152+
Collections.emptyList(), null, rootNode).returning(rootNode).build();
153+
154+
// we want to ensure that the pattern occurs three times but do not care about the order
155+
// of the relationship types
156+
Pattern pattern = Pattern.compile(
157+
"\\[:(`CORNERED`\\|`ROUND`|`ROUND`\\|`CORNERED`)\\*.*" +
158+
"\\[:(`CORNERED`\\|`ROUND`|`ROUND`\\|`CORNERED`)\\*.*" +
159+
"\\[:(`CORNERED`\\|`ROUND`|`ROUND`\\|`CORNERED`)\\*");
160+
161+
String renderedStatement = Renderer.getDefaultRenderer().render(statement);
162+
assertThat(renderedStatement).containsPattern(pattern);
163+
}
164+
165+
@Test
166+
void shouldCreateDynamicRelationshipPathQueryForStringsWithWildcardRelationships() {
167+
Neo4jPersistentEntity<?> persistentEntity = new Neo4jMappingContext()
168+
.getPersistentEntity(CyclicEntityWithStringDynamicRelationship1.class);
169+
170+
org.neo4j.cypherdsl.core.Node rootNode = Cypher.anyNode(Constants.NAME_OF_ROOT_NODE);
171+
Statement statement = CypherGenerator.INSTANCE.createPathMatchWithCondition(null, persistentEntity,
172+
Collections.emptyList(), null, rootNode).returning(rootNode).build();
173+
174+
Pattern pattern = Pattern.compile(
175+
"\\[\\*0\\.\\.1].*" +
176+
"\\[\\*0\\.\\.1].*" +
177+
"\\[\\*0\\.\\.].*");
178+
179+
String renderedStatement = Renderer.getDefaultRenderer().render(statement);
180+
assertThat(renderedStatement).containsPattern(pattern);
181+
}
182+
142183
private static Stream<Arguments> pageables() {
143184
return Stream.of(
144185
Arguments.of(Sort.by("a", "b").and(
@@ -219,4 +260,41 @@ private static class MultipleLabelEntity2 {
219260
private Map<String, MultipleLabelEntity2> dynamicRelationships;
220261
}
221262

263+
enum CyclicRelationship {
264+
ROUND,
265+
CORNERED
266+
}
267+
268+
@Node
269+
private static class CyclicEntityWithEnumeratedDynamicRelationship1 {
270+
271+
@Id private Long id;
272+
273+
private Map<CyclicRelationship, CyclicEntityWithEnumeratedDynamicRelationship2> dynamicRelationship;
274+
}
275+
276+
@Node
277+
private static class CyclicEntityWithEnumeratedDynamicRelationship2 {
278+
279+
@Id private Long id;
280+
281+
private Map<CyclicRelationship, CyclicEntityWithEnumeratedDynamicRelationship1> dynamicRelationship;
282+
}
283+
284+
@Node
285+
private static class CyclicEntityWithStringDynamicRelationship1 {
286+
287+
@Id private Long id;
288+
289+
private Map<String, CyclicEntityWithStringDynamicRelationship2> dynamicRelationship;
290+
}
291+
292+
@Node
293+
private static class CyclicEntityWithStringDynamicRelationship2 {
294+
295+
@Id private Long id;
296+
297+
private Map<String, CyclicEntityWithStringDynamicRelationship1> dynamicRelationship;
298+
}
299+
222300
}

0 commit comments

Comments
 (0)