1717
1818import static org .assertj .core .api .Assertions .assertThat ;
1919
20- import java .io .BufferedReader ;
2120import java .io .IOException ;
22- import java .io .InputStreamReader ;
2321import java .util .Collections ;
2422import java .util .HashMap ;
2523import java .util .List ;
2624import java .util .Map ;
25+ import java .util .Optional ;
2726import java .util .function .Function ;
2827import java .util .stream .Collectors ;
2928
3736import org .springframework .data .neo4j .config .AbstractNeo4jConfig ;
3837import org .springframework .data .neo4j .core .Neo4jTemplate ;
3938import org .springframework .data .neo4j .integration .movies .shared .Actor ;
39+ import org .springframework .data .neo4j .integration .movies .shared .CypherUtils ;
4040import 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 ;
4143import org .springframework .data .neo4j .integration .movies .shared .Person ;
4244import org .springframework .data .neo4j .repository .Neo4jRepository ;
4345import org .springframework .data .neo4j .repository .config .EnableNeo4jRepositories ;
@@ -61,37 +63,16 @@ class AdvancedMappingIT {
6163 @ BeforeAll
6264 static void setupData (@ Autowired Driver driver ) throws IOException {
6365
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 ()) {
6767 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 );
9070 }
9171 }
9272
9373 interface MovieProjectionWithActorProjection {
9474 String getTitle ();
75+
9576 List <ActorProjection > getActors ();
9677
9778 interface ActorProjection {
@@ -141,6 +122,38 @@ interface MovieRepository extends Neo4jRepository<Movie, String> {
141122 List <Movie > customPathQueryMoviesFind (@ Param ("title" ) String title );
142123 }
143124
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+
144157 @ Test // GH-2117
145158 void bothCyclicAndNonCyclicRelationshipsAreExcludedFromProjections (@ Autowired MovieRepository movieRepository ) {
146159
@@ -164,12 +177,14 @@ void bothCyclicAndNonCyclicRelationshipsAreExcludedFromDTOProjections(@Autowired
164177 }
165178
166179 @ Test // GH-2117
167- void bothCyclicAndNonCyclicRelationshipsAreExcludedFromProjectionsWithProjections (@ Autowired MovieRepository movieRepository ) {
180+ void bothCyclicAndNonCyclicRelationshipsAreExcludedFromProjectionsWithProjections (
181+ @ Autowired MovieRepository movieRepository ) {
168182
169183 // The movie domain is a good fit for this test
170184 // as the cyclic dependencies is pretty slow to retrieve from Neo4j
171185 // this does OOM in most setups.
172- MovieProjectionWithActorProjection projection = movieRepository .findProjectionWithProjectionByTitle ("The Matrix" );
186+ MovieProjectionWithActorProjection projection = movieRepository
187+ .findProjectionWithProjectionByTitle ("The Matrix" );
173188 assertThat (projection .getTitle ()).isNotNull ();
174189 assertThat (projection .getActors ()).isNotEmpty ();
175190 }
@@ -192,15 +207,18 @@ void bothStartAndEndNodeOfPathsMustBeLookedAt(@Autowired Neo4jTemplate template)
192207 @ Test // GH-2114
193208 void directionAndTypeLessPathMappingShouldWork (@ Autowired Neo4jTemplate template ) {
194209
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 );
196212 assertThat (people ).hasSize (6 );
197213 }
198214
199215 @ Test // GH-2114
200216 void mappingOfAPathWithOddNumberOfElementsShouldWorkFromStartToEnd (@ Autowired Neo4jTemplate template ) {
201217
202218 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 )
204222 .stream ().collect (Collectors .toMap (Movie ::getTitle , Function .identity ()));
205223 assertThat (movies ).hasSize (3 );
206224
@@ -217,7 +235,9 @@ void mappingOfAPathWithOddNumberOfElementsShouldWorkFromStartToEnd(@Autowired Ne
217235 void mappingOfAPathWithEventNumberOfElementsShouldWorkFromStartToEnd (@ Autowired Neo4jTemplate template ) {
218236
219237 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 )
221241 .stream ().collect (Collectors .toMap (Movie ::getTitle , Function .identity ()));
222242 assertThat (movies ).hasSize (3 );
223243
0 commit comments