Skip to content

Commit df5cbcd

Browse files
DATAGRAPH-1397 - Create correct target collections for relationships.
The target type of the collection for relationships with properties has not been taken into account and this commit fixes it. The commit also addresses a possible issue in the Cypher generator for relationships when several mapping to different nodes with the same type exists. The loop needs to continue in case one has already been found, not left. Co-authored-by: Gerrit Meier <[email protected]>
1 parent 7f75f13 commit df5cbcd

File tree

6 files changed

+118
-15
lines changed

6 files changed

+118
-15
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,11 +401,11 @@ private String[] collectAllRelationshipTypes(NodeDescription<?> nodeDescription)
401401
for (RelationshipDescription relationshipDescription : nodeDescription.getRelationships()) {
402402
String relationshipType = relationshipDescription.getType();
403403
if (relationshipTypes.contains(relationshipType)) {
404-
break;
404+
continue;
405405
}
406406
if (relationshipDescription.isDynamic()) {
407407
relationshipTypes.clear();
408-
break;
408+
continue;
409409
}
410410
relationshipTypes.add(relationshipType);
411411
collectAllRelationshipTypes(relationshipDescription.getTarget(), relationshipTypes);

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -507,13 +507,10 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
507507
}
508508

509509
if (persistentProperty.getTypeInformation().isCollectionLike()) {
510-
if (relationshipDescription.hasRelationshipProperties()) {
511-
return Optional.of(relationshipsAndProperties);
512-
} else if (persistentProperty.getType().equals(Set.class)) {
513-
return Optional.of(new HashSet(value));
514-
} else {
515-
return Optional.of(value);
516-
}
510+
List<Object> returnedValues = relationshipDescription.hasRelationshipProperties() ? relationshipsAndProperties : value;
511+
Collection<Object> target = CollectionFactory.createCollection(persistentProperty.getRawType(), persistentProperty.getComponentType(), returnedValues.size());
512+
target.addAll(returnedValues);
513+
return Optional.of(target);
517514
} else {
518515
if (relationshipDescription.isDynamic()) {
519516
return Optional.ofNullable(dynamicValue.isEmpty() ? null : dynamicValue);

src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
7777
import org.springframework.data.neo4j.core.DatabaseSelection;
7878
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
79+
import org.springframework.data.neo4j.core.Neo4jTemplate;
7980
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
8081
import org.springframework.data.neo4j.integration.imperative.repositories.PersonRepository;
8182
import org.springframework.data.neo4j.integration.imperative.repositories.ThingRepository;
@@ -86,6 +87,7 @@
8687
import org.springframework.data.neo4j.integration.shared.BidirectionalEnd;
8788
import org.springframework.data.neo4j.integration.shared.BidirectionalStart;
8889
import org.springframework.data.neo4j.integration.shared.Club;
90+
import org.springframework.data.neo4j.integration.shared.ClubRelationship;
8991
import org.springframework.data.neo4j.integration.shared.DeepRelationships;
9092
import org.springframework.data.neo4j.integration.shared.EntityWithConvertedId;
9193
import org.springframework.data.neo4j.integration.shared.Friend;
@@ -100,6 +102,7 @@
100102
import org.springframework.data.neo4j.integration.shared.PersonWithNoConstructor;
101103
import org.springframework.data.neo4j.integration.shared.PersonWithRelationship;
102104
import org.springframework.data.neo4j.integration.shared.PersonWithRelationshipWithProperties;
105+
import org.springframework.data.neo4j.integration.shared.PersonWithRelationshipWithProperties2;
103106
import org.springframework.data.neo4j.integration.shared.PersonWithWither;
104107
import org.springframework.data.neo4j.integration.shared.Pet;
105108
import org.springframework.data.neo4j.integration.shared.SimilarThing;
@@ -903,6 +906,33 @@ void findEntityWithRelationshipWithAssignedId(@Autowired PetRepository repositor
903906
@Nested
904907
class RelationshipProperties extends IntegrationTestBase {
905908

909+
@Test // DATAGRAPH-1397
910+
void shouldBeStorableOnSets(
911+
@Autowired Neo4jTemplate template) {
912+
913+
long personId;
914+
915+
try (Session session = createSession()) {
916+
Record record = session.run("CREATE (n:PersonWithRelationshipWithProperties2{name:'Freddie'}),"
917+
+ " (n)-[l1:LIKES "
918+
+ "{since: 1995, active: true, localDate: date('1995-02-26'), myEnum: 'SOMETHING', point: point({x: 0, y: 1})}"
919+
+ "]->(h1:Hobby{name:'Music'}), "
920+
+ "(n)-[l2:LIKES "
921+
+ "{since: 2000, active: false, localDate: date('2000-06-28'), myEnum: 'SOMETHING_DIFFERENT', point: point({x: 2, y: 3})}"
922+
+ "]->(h2:Hobby{name:'Something else'})"
923+
+ "RETURN n, h1, h2").single();
924+
925+
Node personNode = record.get("n").asNode();
926+
personId = personNode.id();
927+
}
928+
929+
Optional<PersonWithRelationshipWithProperties2> optionalPerson = template.findById(personId, PersonWithRelationshipWithProperties2.class);
930+
assertThat(optionalPerson).hasValueSatisfying(person -> {
931+
assertThat(person.getName()).isEqualTo("Freddie");
932+
assertThat(person.getHobbies()).hasSize(2).extracting(LikesHobbyRelationship::getSince).containsExactlyInAnyOrder(1995, 2000);
933+
});
934+
}
935+
906936
@Test
907937
void findEntityWithRelationshipWithProperties(
908938
@Autowired PersonWithRelationshipWithPropertiesRepository repository) {
@@ -913,11 +943,16 @@ void findEntityWithRelationshipWithProperties(
913943

914944
try (Session session = createSession()) {
915945
Record record = session.run("CREATE (n:PersonWithRelationshipWithProperties{name:'Freddie'}),"
916-
+ " (n)-[l1:LIKES"
946+
+ " (n)-[l1:LIKES "
917947
+ "{since: 1995, active: true, localDate: date('1995-02-26'), myEnum: 'SOMETHING', point: point({x: 0, y: 1})}"
918-
+ "]->(h1:Hobby{name:'Music'})," + " (n)-[l2:LIKES"
948+
+ "]->(h1:Hobby{name:'Music'}), "
949+
+ "(n)-[l2:LIKES "
919950
+ "{since: 2000, active: false, localDate: date('2000-06-28'), myEnum: 'SOMETHING_DIFFERENT', point: point({x: 2, y: 3})}"
920-
+ "]->(h2:Hobby{name:'Something else'})" + "RETURN n, h1, h2").single();
951+
+ "]->(h2:Hobby{name:'Something else'}), "
952+
+ "(n) - [:OWNS] -> (p:Pet {name: 'A Pet'}), "
953+
+ "(n) - [:OWNS {place: 'The place to be'}] -> (c1:Club {name: 'Berlin Mitte'}), "
954+
+ "(n) - [:OWNS {place: 'Whatever'}] -> (c2:Club {name: 'Schachklub'}) "
955+
+ "RETURN n, h1, h2").single();
921956

922957
Node personNode = record.get("n").asNode();
923958
Node hobbyNode1 = record.get("h1").asNode();
@@ -932,6 +967,7 @@ void findEntityWithRelationshipWithProperties(
932967
assertThat(optionalPerson).isPresent();
933968
PersonWithRelationshipWithProperties person = optionalPerson.get();
934969
assertThat(person.getName()).isEqualTo("Freddie");
970+
assertThat(person.getPets()).hasSize(1).first().extracting(Pet::getName).isEqualTo("A Pet");
935971

936972
Hobby hobby1 = new Hobby();
937973
hobby1.setName("Music");
@@ -955,17 +991,22 @@ void findEntityWithRelationshipWithProperties(
955991
assertThat(hobbies).containsExactlyInAnyOrder(rel1, rel2);
956992
assertThat(hobbies.get(hobbies.indexOf(rel1)).getHobby()).isEqualTo(hobby1);
957993
assertThat(hobbies.get(hobbies.indexOf(rel2)).getHobby()).isEqualTo(hobby2);
994+
995+
assertThat(person.getClubs()).hasSize(2)
996+
.extracting(ClubRelationship::getPlace)
997+
.containsExactlyInAnyOrder("The place to be", "Whatever");
958998
}
959999

9601000
@Test
961-
void findEntityWithRelationshipWithPropertiesScalar(
962-
@Autowired PersonWithRelationshipWithPropertiesRepository repository) {
1001+
void findEntityWithRelationshipWithPropertiesScalar(@Autowired PersonWithRelationshipWithPropertiesRepository repository) {
9631002

9641003
long personId;
9651004

9661005
try (Session session = createSession()) {
9671006
Record record = session.run("CREATE (n:PersonWithRelationshipWithProperties{name:'Freddie'}),"
968-
+ " (n)-[:WORKS_IN{since: 1995}]->(:Club{name:'Blubb'})"
1007+
+ " (n)-[:WORKS_IN{since: 1995}]->(:Club{name:'Blubb'}),"
1008+
+ "(n) - [:OWNS {place: 'The place to be'}] -> (c1:Club {name: 'Berlin Mitte'}), "
1009+
+ "(n) - [:OWNS {place: 'Whatever'}] -> (c2:Club {name: 'Schachklub'}) "
9691010
+ "RETURN n").single();
9701011

9711012
Node personNode = record.get("n").asNode();

src/test/java/org/springframework/data/neo4j/integration/shared/LikesHobbyRelationship.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ public void setHobby(Hobby hobby) {
9191
this.hobby = hobby;
9292
}
9393

94+
public Integer getSince() {
95+
return since;
96+
}
97+
9498
/**
9599
* The missing javadoc
96100
*/

src/test/java/org/springframework/data/neo4j/integration/shared/PersonWithRelationshipWithProperties.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public class PersonWithRelationshipWithProperties {
4040

4141
@Relationship("OWNS") private Set<Pet> pets;
4242

43+
@Relationship("OWNS") private List<ClubRelationship> clubs;
44+
4345
public PersonWithRelationshipWithProperties(String name) {
4446
this.name = name;
4547
}
@@ -64,4 +66,11 @@ public WorksInClubRelationship getClub() {
6466
return club;
6567
}
6668

69+
public Set<Pet> getPets() {
70+
return pets;
71+
}
72+
73+
public List<ClubRelationship> getClubs() {
74+
return clubs;
75+
}
6776
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2011-2020 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.shared;
17+
18+
import java.util.Set;
19+
20+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
21+
import org.springframework.data.neo4j.core.schema.Id;
22+
import org.springframework.data.neo4j.core.schema.Node;
23+
import org.springframework.data.neo4j.core.schema.Relationship;
24+
25+
/**
26+
* @author Michael J. Simons
27+
*/
28+
@Node
29+
public class PersonWithRelationshipWithProperties2 {
30+
31+
@Id @GeneratedValue private Long id;
32+
33+
private final String name;
34+
35+
@Relationship("LIKES") private Set<LikesHobbyRelationship> hobbies;
36+
37+
public PersonWithRelationshipWithProperties2(String name) {
38+
this.name = name;
39+
}
40+
41+
public Long getId() {
42+
return id;
43+
}
44+
45+
public String getName() {
46+
return name;
47+
}
48+
49+
public Set<LikesHobbyRelationship> getHobbies() {
50+
return hobbies;
51+
}
52+
}

0 commit comments

Comments
 (0)