Skip to content

Commit 7e9011d

Browse files
GH-2117 - Exclude relationships from projections when not projected.
This fixes #2117.
1 parent 60b316a commit 7e9011d

File tree

3 files changed

+64
-6
lines changed

3 files changed

+64
-6
lines changed

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ private MapProjection projectPropertiesAndRelationships(NodeDescription<?> nodeD
393393
List<Object> contentOfProjection = new ArrayList<>(propertiesProjection);
394394

395395
Collection<RelationshipDescription> relationships = getRelationshipDescriptionsUpAndDown(nodeDescription);
396+
relationships.removeIf(r -> !includeProperty.test(r.getFieldName()));
396397

397398
if (nodeDescription.containsPossibleCircles()) {
398399
Node node = anyNode(nodeName);
@@ -401,8 +402,7 @@ private MapProjection projectPropertiesAndRelationships(NodeDescription<?> nodeD
401402
contentOfProjection.add(Constants.NAME_OF_PATHS);
402403
contentOfProjection.add(Cypher.listBasedOn(p).returning(p));
403404
} else {
404-
contentOfProjection.addAll(
405-
generateListsFor(relationships, nodeName, includeProperty, processedRelationships));
405+
contentOfProjection.addAll(generateListsFor(relationships, nodeName, processedRelationships));
406406
}
407407
return Cypher.anyNode(nodeName).project(contentOfProjection);
408408
}
@@ -599,16 +599,13 @@ private List<Object> projectNodeProperties(NodeDescription<?> nodeDescription, S
599599
* @see CypherGenerator#projectNodeProperties
600600
*/
601601
private List<Object> generateListsFor(Collection<RelationshipDescription> relationships, SymbolicName nodeName,
602-
Predicate<String> includeField, List<RelationshipDescription> processedRelationships) {
602+
List<RelationshipDescription> processedRelationships) {
603603

604604
List<Object> mapProjectionLists = new ArrayList<>();
605605

606606
for (RelationshipDescription relationshipDescription : relationships) {
607607

608608
String fieldName = relationshipDescription.getFieldName();
609-
if (!includeField.test(fieldName)) {
610-
continue;
611-
}
612609

613610
// if we already processed the other way before, do not try to jump in the infinite loop
614611
// unless it is a root node relationship

src/main/java/org/springframework/data/neo4j/repository/query/CypherQueryCreator.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ private class PropertyPathWrapper {
162162
this.propertyPath = propertyPath;
163163
}
164164

165+
166+
165167
public PersistentPropertyPath<?> getPropertyPath() {
166168
return propertyPath;
167169
}

src/test/java/org/springframework/data/neo4j/integration/movies/AdvancedMappingIT.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.springframework.context.annotation.Configuration;
3737
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
3838
import org.springframework.data.neo4j.core.Neo4jTemplate;
39+
import org.springframework.data.neo4j.repository.Neo4jRepository;
40+
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
3941
import org.springframework.data.neo4j.test.Neo4jExtension;
4042
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
4143
import org.springframework.transaction.annotation.EnableTransactionManagement;
@@ -83,6 +85,62 @@ static void setupData(@Autowired Driver driver) throws IOException {
8385
}
8486
}
8587

88+
interface MovieProjection {
89+
90+
String getTitle();
91+
92+
List<Actor> getActors();
93+
}
94+
95+
static class MovieDTO {
96+
97+
private final String title;
98+
99+
private final List<Actor> actors;
100+
101+
MovieDTO(String title, List<Actor> actors) {
102+
this.title = title;
103+
this.actors = actors;
104+
}
105+
106+
public String getTitle() {
107+
return title;
108+
}
109+
110+
public List<Actor> getActors() {
111+
return actors;
112+
}
113+
}
114+
115+
interface MovieRepository extends Neo4jRepository<Movie, String> {
116+
117+
MovieProjection findProjectionByTitle(String title);
118+
119+
MovieDTO findDTOByTitle(String title);
120+
}
121+
122+
@Test // GH-2117
123+
void bothCyclicAndNonCyclicRelationshipsAreExcludedFromProjections(@Autowired MovieRepository movieRepository) {
124+
125+
// The movie domain is a good fit for this test
126+
// as the cyclic dependencies is pretty slow to retrieve from Neo4j
127+
// this does OOM in most setups.
128+
MovieProjection projection = movieRepository.findProjectionByTitle("The Matrix");
129+
assertThat(projection.getTitle()).isNotNull();
130+
assertThat(projection.getActors()).isNotEmpty();
131+
}
132+
133+
@Test // GH-2117
134+
void bothCyclicAndNonCyclicRelationshipsAreExcludedFromDTOProjections(@Autowired MovieRepository movieRepository) {
135+
136+
// The movie domain is a good fit for this test
137+
// as the cyclic dependencies is pretty slow to retrieve from Neo4j
138+
// this does OOM in most setups.
139+
MovieDTO dtoProjection = movieRepository.findDTOByTitle("The Matrix");
140+
assertThat(dtoProjection.getTitle()).isNotNull();
141+
assertThat(dtoProjection.getActors()).isNotEmpty();
142+
}
143+
86144
@Test // GH-2114
87145
void bothStartAndEndNodeOfPathsMustBeLookedAt(@Autowired Neo4jTemplate template) {
88146

@@ -237,6 +295,7 @@ void pathMappingWithAdditionalInformationShouldWork(@Autowired Neo4jTemplate tem
237295

238296
@Configuration
239297
@EnableTransactionManagement
298+
@EnableNeo4jRepositories(considerNestedRepositories = true)
240299
static class Config extends AbstractNeo4jConfig {
241300

242301
@Bean

0 commit comments

Comments
 (0)