17
17
18
18
import static org .assertj .core .api .Assertions .assertThat ;
19
19
20
- import java .io .BufferedReader ;
21
20
import java .io .IOException ;
22
- import java .io .InputStreamReader ;
23
21
import java .util .Collections ;
24
22
import java .util .HashMap ;
25
23
import java .util .List ;
26
24
import java .util .Map ;
25
+ import java .util .Optional ;
27
26
import java .util .function .Function ;
28
27
import java .util .stream .Collectors ;
29
28
37
36
import org .springframework .data .neo4j .config .AbstractNeo4jConfig ;
38
37
import org .springframework .data .neo4j .core .Neo4jTemplate ;
39
38
import org .springframework .data .neo4j .integration .movies .shared .Actor ;
39
+ import org .springframework .data .neo4j .integration .movies .shared .CypherUtils ;
40
40
import org .springframework .data .neo4j .integration .movies .shared .Movie ;
41
+ import org .springframework .data .neo4j .integration .movies .shared .Organisation ;
42
+ import org .springframework .data .neo4j .integration .movies .shared .Partner ;
41
43
import org .springframework .data .neo4j .integration .movies .shared .Person ;
42
44
import org .springframework .data .neo4j .repository .Neo4jRepository ;
43
45
import org .springframework .data .neo4j .repository .config .EnableNeo4jRepositories ;
@@ -61,37 +63,16 @@ class AdvancedMappingIT {
61
63
@ BeforeAll
62
64
static void setupData (@ Autowired Driver driver ) throws IOException {
63
65
64
- try (BufferedReader moviesReader = new BufferedReader (
65
- new InputStreamReader (AdvancedMappingIT .class .getResourceAsStream ("/data/movies.cypher" )));
66
- Session session = driver .session ()) {
66
+ try (Session session = driver .session ()) {
67
67
session .run ("MATCH (n) DETACH DELETE n" ).consume ();
68
- String moviesCypher = moviesReader .lines ().collect (Collectors .joining (" " ));
69
- session .run (moviesCypher ).consume ();
70
- session .run ("MATCH (l1:Person {name: 'Lilly Wachowski'})\n "
71
- + "MATCH (l2:Person {name: 'Lana Wachowski'})\n "
72
- + "CREATE (l1) - [s:IS_SIBLING_OF] -> (l2)\n "
73
- + "RETURN *" ).consume ();
74
- session .run ("MATCH (m1:Movie {title: 'The Matrix'})\n "
75
- + "MATCH (m2:Movie {title: 'The Matrix Reloaded'})\n "
76
- + "MATCH (m3:Movie {title: 'The Matrix Revolutions'})\n "
77
- + "CREATE (m2) - [:IS_SEQUEL_OF] -> (m1)\n "
78
- + "CREATE (m3) - [:IS_SEQUEL_OF] -> (m2)\n "
79
- + "RETURN *" ).consume ();
80
- session .run ("MATCH (m1:Movie {title: 'The Matrix'})\n "
81
- + "MATCH (m2:Movie {title: 'The Matrix Reloaded'})\n "
82
- + "CREATE (p:Person {name: 'Gloria Foster'})\n "
83
- + "CREATE (p) -[:ACTED_IN {roles: ['The Oracle']}] -> (m1)\n "
84
- + "CREATE (p) -[:ACTED_IN {roles: ['The Oracle']}] -> (m2)\n "
85
- + "RETURN *" ).consume ();
86
- session .run ("MATCH (m3:Movie {title: 'The Matrix Revolutions'})\n "
87
- + "CREATE (p:Person {name: 'Mary Alice'})\n "
88
- + "CREATE (p) -[:ACTED_IN {roles: ['The Oracle']}] -> (m3)\n "
89
- + "RETURN *" ).consume ();
68
+ CypherUtils .loadCypherFromResource ("/data/movies.cypher" , session );
69
+ CypherUtils .loadCypherFromResource ("/data/orgstructure.cypher" , session );
90
70
}
91
71
}
92
72
93
73
interface MovieProjectionWithActorProjection {
94
74
String getTitle ();
75
+
95
76
List <ActorProjection > getActors ();
96
77
97
78
interface ActorProjection {
@@ -141,6 +122,38 @@ interface MovieRepository extends Neo4jRepository<Movie, String> {
141
122
List <Movie > customPathQueryMoviesFind (@ Param ("title" ) String title );
142
123
}
143
124
125
+ @ Test // GH-1906
126
+ void nestedSelfRelationshipsFromCustomQueryShouldWork (@ Autowired Neo4jTemplate template ) {
127
+
128
+ Optional <Partner > optionalPartner = template .findOne (
129
+ "MATCH p=(partner:Partner {code: $partnerCode})-[:CHILD_ORGANISATIONS*0..4]->(org:Organisation) \n "
130
+ + "UNWIND nodes(p) as node UNWIND relationships(p) as rel\n "
131
+ + "RETURN partner, collect(distinct node), collect(distinct rel)" ,
132
+ Collections .singletonMap ("partnerCode" , "partner-one" ), Partner .class );
133
+
134
+ assertThat (optionalPartner ).hasValueSatisfying (p -> {
135
+ assertThat (p .getName ()).isEqualTo ("partner one" );
136
+
137
+ assertThat (p .getOrganisations ()).hasSize (1 );
138
+ Organisation org1 = p .getOrganisations ().get (0 );
139
+
140
+ assertThat (org1 .getCode ()).isEqualTo ("org-1" );
141
+ Map <String , Organisation > org1Childs = org1 .getOrganisations ().stream ()
142
+ .collect (Collectors .toMap (Organisation ::getCode , Function .identity ()));
143
+ assertThat (org1Childs ).hasSize (2 );
144
+
145
+ assertThat (org1Childs ).hasEntrySatisfying ("org-2" , o -> assertThat (o .getOrganisations ()).hasSize (1 ));
146
+ assertThat (org1Childs ).hasEntrySatisfying ("org-6" , o -> assertThat (o .getOrganisations ()).isEmpty ());
147
+
148
+ Organisation org3 = org1Childs .get ("org-2" ).getOrganisations ().get (0 );
149
+ assertThat (org3 .getCode ()).isEqualTo ("org-3" );
150
+
151
+ Map <String , Organisation > org3Childs = org3 .getOrganisations ().stream ()
152
+ .collect (Collectors .toMap (Organisation ::getCode , Function .identity ()));
153
+ assertThat (org3Childs ).containsKeys ("org-4" , "org-5" );
154
+ });
155
+ }
156
+
144
157
@ Test // GH-2117
145
158
void bothCyclicAndNonCyclicRelationshipsAreExcludedFromProjections (@ Autowired MovieRepository movieRepository ) {
146
159
@@ -164,12 +177,14 @@ void bothCyclicAndNonCyclicRelationshipsAreExcludedFromDTOProjections(@Autowired
164
177
}
165
178
166
179
@ Test // GH-2117
167
- void bothCyclicAndNonCyclicRelationshipsAreExcludedFromProjectionsWithProjections (@ Autowired MovieRepository movieRepository ) {
180
+ void bothCyclicAndNonCyclicRelationshipsAreExcludedFromProjectionsWithProjections (
181
+ @ Autowired MovieRepository movieRepository ) {
168
182
169
183
// The movie domain is a good fit for this test
170
184
// as the cyclic dependencies is pretty slow to retrieve from Neo4j
171
185
// this does OOM in most setups.
172
- MovieProjectionWithActorProjection projection = movieRepository .findProjectionWithProjectionByTitle ("The Matrix" );
186
+ MovieProjectionWithActorProjection projection = movieRepository
187
+ .findProjectionWithProjectionByTitle ("The Matrix" );
173
188
assertThat (projection .getTitle ()).isNotNull ();
174
189
assertThat (projection .getActors ()).isNotEmpty ();
175
190
}
@@ -192,15 +207,18 @@ void bothStartAndEndNodeOfPathsMustBeLookedAt(@Autowired Neo4jTemplate template)
192
207
@ Test // GH-2114
193
208
void directionAndTypeLessPathMappingShouldWork (@ Autowired Neo4jTemplate template ) {
194
209
195
- List <Person > people = template .findAll ("MATCH p=(:Person)-[]-(:Person) RETURN p" , Collections .emptyMap (), Person .class );
210
+ List <Person > people = template
211
+ .findAll ("MATCH p=(:Person)-[]-(:Person) RETURN p" , Collections .emptyMap (), Person .class );
196
212
assertThat (people ).hasSize (6 );
197
213
}
198
214
199
215
@ Test // GH-2114
200
216
void mappingOfAPathWithOddNumberOfElementsShouldWorkFromStartToEnd (@ Autowired Neo4jTemplate template ) {
201
217
202
218
Map <String , Movie > movies = template
203
- .findAll ("MATCH p=shortestPath((:Person {name: 'Mary Alice'})-[*]-(:Person {name: 'Emil Eifrem'})) RETURN p" , Collections .emptyMap (), Movie .class )
219
+ .findAll (
220
+ "MATCH p=shortestPath((:Person {name: 'Mary Alice'})-[*]-(:Person {name: 'Emil Eifrem'})) RETURN p" ,
221
+ Collections .emptyMap (), Movie .class )
204
222
.stream ().collect (Collectors .toMap (Movie ::getTitle , Function .identity ()));
205
223
assertThat (movies ).hasSize (3 );
206
224
@@ -217,7 +235,9 @@ void mappingOfAPathWithOddNumberOfElementsShouldWorkFromStartToEnd(@Autowired Ne
217
235
void mappingOfAPathWithEventNumberOfElementsShouldWorkFromStartToEnd (@ Autowired Neo4jTemplate template ) {
218
236
219
237
Map <String , Movie > movies = template
220
- .findAll ("MATCH p=shortestPath((:Movie {title: 'The Matrix Revolutions'})-[*]-(:Person {name: 'Emil Eifrem'})) RETURN p" , Collections .emptyMap (), Movie .class )
238
+ .findAll (
239
+ "MATCH p=shortestPath((:Movie {title: 'The Matrix Revolutions'})-[*]-(:Person {name: 'Emil Eifrem'})) RETURN p" ,
240
+ Collections .emptyMap (), Movie .class )
221
241
.stream ().collect (Collectors .toMap (Movie ::getTitle , Function .identity ()));
222
242
assertThat (movies ).hasSize (3 );
223
243
0 commit comments