Skip to content

Commit ce3dc98

Browse files
committed
GH-2727 - Fix multiple levels of projecting properties mapping.
Closes #2727
1 parent f330c76 commit ce3dc98

File tree

12 files changed

+426
-16
lines changed

12 files changed

+426
-16
lines changed

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

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -744,21 +744,7 @@ private List<Object> projectNodeProperties(PropertyFilter.RelaxedPropertyPath pa
744744
if (property.isDynamicLabels() || property.isComposite()) {
745745
continue;
746746
}
747-
PropertyFilter.RelaxedPropertyPath from;
748-
749-
if (relationshipDescription == null) {
750-
from = parentPath.append(property.getFieldName());
751-
752-
} else if (relationshipDescription.hasRelationshipProperties()) {
753-
@SuppressWarnings("ConstantConditions") // All prerequisites have been checked by the persistence context at this point.
754-
String relationshipPropertyTargetNodeFieldName =
755-
((Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity())
756-
.getPersistentProperty(TargetNode.class).getFieldName();
757-
758-
from = parentPath.append(relationshipPropertyTargetNodeFieldName + "." + property.getFieldName());
759-
} else {
760-
from = parentPath.append(property.getFieldName());
761-
}
747+
PropertyFilter.RelaxedPropertyPath from = parentPath.append(property.getFieldName());
762748

763749
if (!includeField.test(from)) {
764750
continue;
@@ -828,7 +814,10 @@ private void generateListFor(PropertyFilter.RelaxedPropertyPath parentPath, Neo4
828814
Neo4jPersistentEntity<?> endNodeDescription = (Neo4jPersistentEntity<?>) relationshipDescription.getTarget();
829815

830816
processedRelationships.add(relationshipDescription);
831-
PropertyFilter.RelaxedPropertyPath newParentPath = parentPath.append(relationshipDescription.getFieldName());
817+
PropertyFilter.RelaxedPropertyPath newParentPath = relationshipDescription.hasRelationshipProperties()
818+
? parentPath.append(relationshipDescription.getFieldName()).append(((Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity())
819+
.getPersistentProperty(TargetNode.class).getFieldName())
820+
: parentPath.append(relationshipDescription.getFieldName());
832821

833822
if (relationshipDescription.isDynamic()) {
834823
Relationship relationship = relationshipDescription.isOutgoing()

src/test/java/org/springframework/data/neo4j/integration/issues/IssuesIT.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@
145145
import org.springframework.data.neo4j.integration.issues.qbe.A;
146146
import org.springframework.data.neo4j.integration.issues.qbe.ARepository;
147147
import org.springframework.data.neo4j.integration.issues.qbe.B;
148+
import org.springframework.data.neo4j.integration.issues.gh2727.FirstLevelEntity;
149+
import org.springframework.data.neo4j.integration.issues.gh2727.FirstLevelEntityRepository;
150+
import org.springframework.data.neo4j.integration.issues.gh2727.FirstLevelProjection;
151+
import org.springframework.data.neo4j.integration.issues.gh2727.SecondLevelEntity;
152+
import org.springframework.data.neo4j.integration.issues.gh2727.SecondLevelEntityRelationship;
153+
import org.springframework.data.neo4j.integration.issues.gh2727.ThirdLevelEntity;
154+
import org.springframework.data.neo4j.integration.issues.gh2727.ThirdLevelEntityRelationship;
148155
import org.springframework.data.neo4j.integration.misc.ConcreteImplementationTwo;
149156
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
150157
import org.springframework.data.neo4j.repository.query.QueryFragmentsAndParameters;
@@ -1030,6 +1037,59 @@ void throwCyclicMappingDependencyExceptionOnSelfReference(@Autowired GH2622Repos
10301037

10311038
}
10321039

1040+
@Test
1041+
@Tag("GH-2727")
1042+
void mapsProjectionChainWithRelationshipProperties(@Autowired FirstLevelEntityRepository firstLevelEntityRepository) {
1043+
1044+
String secondLevelValue = "someSecondLevelValue";
1045+
String thirdLevelValue = "someThirdLevelValue";
1046+
1047+
final List<SecondLevelEntityRelationship> secondLevelEntityRelationships = new ArrayList<>();
1048+
for (int i = 0; i < 2; i++) {
1049+
final List<ThirdLevelEntityRelationship> thirdLevelEntityRelationships = new ArrayList<>();
1050+
for (int j = 0; j < 3; j++) {
1051+
final ThirdLevelEntity thirdLevelEntity = new ThirdLevelEntity();
1052+
thirdLevelEntity.setSomeValue(thirdLevelValue);
1053+
final ThirdLevelEntityRelationship thirdLevelRelationship = new ThirdLevelEntityRelationship();
1054+
thirdLevelRelationship.setTarget(thirdLevelEntity);
1055+
thirdLevelRelationship.setOrder(j + 1);
1056+
thirdLevelEntityRelationships.add(thirdLevelRelationship);
1057+
}
1058+
1059+
final SecondLevelEntity secondLevelEntity = SecondLevelEntity.builder()
1060+
.thirdLevelEntityRelationshipProperties(thirdLevelEntityRelationships)
1061+
.someValue(secondLevelValue)
1062+
.build();
1063+
1064+
final SecondLevelEntityRelationship secondLevelRelationship = new SecondLevelEntityRelationship();
1065+
secondLevelRelationship.setTarget(secondLevelEntity);
1066+
secondLevelRelationship.setOrder(i + 1);
1067+
secondLevelEntityRelationships.add(secondLevelRelationship);
1068+
}
1069+
1070+
final FirstLevelEntity firstLevelEntity = FirstLevelEntity.builder()
1071+
.secondLevelEntityRelationshipProperties(secondLevelEntityRelationships)
1072+
.name("Test")
1073+
.build();
1074+
1075+
firstLevelEntityRepository.save(firstLevelEntity);
1076+
1077+
FirstLevelProjection firstLevelProjection = firstLevelEntityRepository.findOneById(firstLevelEntity.getId());
1078+
assertThat(firstLevelProjection).isNotNull();
1079+
assertThat(firstLevelProjection.getSecondLevelEntityRelationshipProperties()).hasSize(2)
1080+
.allSatisfy(secondLevelRelationship -> {
1081+
assertThat(secondLevelRelationship.getTarget().getSomeValue().equals(secondLevelValue));
1082+
assertThat(secondLevelRelationship.getOrder()).isGreaterThan(0);
1083+
assertThat(secondLevelRelationship.getTarget().getThirdLevelEntityRelationshipProperties())
1084+
.isNotEmpty()
1085+
.allSatisfy(thirdLevel -> {
1086+
assertThat(thirdLevel.getOrder()).isGreaterThan(0);
1087+
assertThat(thirdLevel.getTarget()).isNotNull();
1088+
assertThat(thirdLevel.getTarget().getSomeValue()).isEqualTo(thirdLevelValue);
1089+
});
1090+
});
1091+
}
1092+
10331093
@Configuration
10341094
@EnableTransactionManagement
10351095
@EnableNeo4jRepositories(namedQueriesLocation = "more-custom-queries.properties")
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2727;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Getter;
20+
import lombok.NoArgsConstructor;
21+
import lombok.Setter;
22+
import lombok.experimental.SuperBuilder;
23+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
24+
import org.springframework.data.neo4j.core.schema.Id;
25+
import org.springframework.data.neo4j.core.schema.Node;
26+
import org.springframework.data.neo4j.core.schema.Relationship;
27+
28+
import java.util.List;
29+
30+
/**
31+
* @author Gerrit Meier
32+
*/
33+
@SuperBuilder
34+
@NoArgsConstructor
35+
@Getter
36+
@Setter
37+
@Node("FirstLevel")
38+
@EqualsAndHashCode(of = {"id"})
39+
public class FirstLevelEntity {
40+
@Id
41+
@GeneratedValue
42+
private Long id;
43+
44+
private String name;
45+
46+
@Relationship("HasSecondLevel")
47+
private List<SecondLevelEntityRelationship> secondLevelEntityRelationshipProperties;
48+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2727;
17+
18+
import org.springframework.data.neo4j.repository.Neo4jRepository;
19+
20+
/**
21+
* @author Gerrit Meier
22+
*/
23+
public interface FirstLevelEntityRepository extends Neo4jRepository<FirstLevelEntity, Long> {
24+
25+
FirstLevelProjection findOneById(Long id);
26+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2727;
17+
18+
import java.util.Set;
19+
20+
/**
21+
* @author Gerrit Meier
22+
*/
23+
public interface FirstLevelProjection {
24+
Long getId();
25+
26+
Set<SecondLevelRelationshipProjection> getSecondLevelEntityRelationshipProperties();
27+
28+
/**
29+
*
30+
*/
31+
interface SecondLevelRelationshipProjection {
32+
Long getId();
33+
34+
SecondLevelProjection getTarget();
35+
36+
Integer getOrder();
37+
}
38+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2727;
17+
18+
import lombok.AllArgsConstructor;
19+
import lombok.Getter;
20+
import lombok.NoArgsConstructor;
21+
import lombok.Setter;
22+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
23+
import org.springframework.data.neo4j.core.schema.Property;
24+
import org.springframework.data.neo4j.core.schema.RelationshipId;
25+
import org.springframework.data.neo4j.core.schema.RelationshipProperties;
26+
import org.springframework.data.neo4j.core.schema.TargetNode;
27+
28+
/**
29+
* @author Gerrit Meier
30+
* @param <T> relationship properties type
31+
*/
32+
@AllArgsConstructor
33+
@NoArgsConstructor
34+
@Getter
35+
@Setter
36+
@RelationshipProperties
37+
public class OrderedRelation<T> implements Comparable<OrderedRelation<T>> {
38+
@RelationshipId
39+
@GeneratedValue
40+
private Long id;
41+
@TargetNode
42+
private T target;
43+
@Property
44+
private Integer order;
45+
46+
@Override
47+
public int compareTo(final OrderedRelation<T> o) {
48+
return order - o.order;
49+
}
50+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2727;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Getter;
20+
import lombok.NoArgsConstructor;
21+
import lombok.Setter;
22+
import lombok.experimental.SuperBuilder;
23+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
24+
import org.springframework.data.neo4j.core.schema.Id;
25+
import org.springframework.data.neo4j.core.schema.Node;
26+
import org.springframework.data.neo4j.core.schema.Relationship;
27+
28+
import java.util.List;
29+
30+
/**
31+
* @author Gerrit Meier
32+
*/
33+
@SuperBuilder
34+
@NoArgsConstructor
35+
@Getter
36+
@Setter
37+
@Node("SecondLevel")
38+
@EqualsAndHashCode(of = {"id"})
39+
public class SecondLevelEntity {
40+
@Id
41+
@GeneratedValue
42+
private Long id;
43+
44+
private String someValue;
45+
46+
@Relationship("HasThirdLevel")
47+
private List<ThirdLevelEntityRelationship> thirdLevelEntityRelationshipProperties;
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2727;
17+
18+
/**
19+
* @author Gerrit Meier
20+
*/
21+
public class SecondLevelEntityRelationship extends OrderedRelation<SecondLevelEntity> {
22+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2727;
17+
18+
import java.util.Set;
19+
20+
/**
21+
* @author Gerrit Meier
22+
*/
23+
public interface SecondLevelProjection {
24+
Long getId();
25+
26+
String getSomeValue();
27+
28+
Set<ThirdLevelRelationshipProjection> getThirdLevelEntityRelationshipProperties();
29+
30+
/**
31+
*
32+
*/
33+
interface ThirdLevelRelationshipProjection {
34+
Long getId();
35+
36+
ThirdLevelProjection getTarget();
37+
38+
Integer getOrder();
39+
}
40+
}

0 commit comments

Comments
 (0)