1717
1818import static org .assertj .core .api .Assertions .assertThat ;
1919
20+ import java .util .AbstractMap ;
2021import java .util .Collection ;
22+ import java .util .Map ;
2123
2224import org .junit .jupiter .api .BeforeEach ;
2325import org .junit .jupiter .api .Test ;
2426import org .neo4j .driver .Driver ;
2527import org .neo4j .driver .Session ;
2628import org .neo4j .driver .Transaction ;
29+ import org .neo4j .driver .Values ;
2730import org .springframework .beans .factory .annotation .Autowired ;
2831import org .springframework .context .annotation .Bean ;
2932import org .springframework .context .annotation .Configuration ;
33+ import org .springframework .data .domain .Page ;
34+ import org .springframework .data .domain .PageRequest ;
35+ import org .springframework .data .domain .Pageable ;
36+ import org .springframework .data .domain .Slice ;
37+ import org .springframework .data .domain .Sort ;
3038import org .springframework .data .neo4j .config .AbstractNeo4jConfig ;
3139import org .springframework .data .neo4j .integration .shared .common .NamesOnly ;
3240import org .springframework .data .neo4j .integration .shared .common .NamesOnlyDto ;
4048
4149/**
4250 * @author Gerrit Meier
51+ * @author Michael J. Simons
4352 */
4453@ Neo4jIntegrationTest
4554class ProjectionIT {
4655
4756 private static final String FIRST_NAME = "Hans" ;
57+ private static final String FIRST_NAME2 = "Lieschen" ;
4858 private static final String LAST_NAME = "Mueller" ;
4959 private static final String CITY = "Braunschweig" ;
5060
@@ -64,8 +74,15 @@ void setup() {
6474
6575 transaction .run ("MATCH (n) detach delete n" );
6676
67- transaction .run ("CREATE (:Person{firstName:'" + FIRST_NAME + "', lastName:'" + LAST_NAME + "'})" + "-[:LIVES_AT]->"
68- + "(:Address{city:'" + CITY + "'})" );
77+ for (Map .Entry <String , String > person : new Map .Entry [] {
78+ new AbstractMap .SimpleEntry (FIRST_NAME , LAST_NAME ),
79+ new AbstractMap .SimpleEntry (FIRST_NAME2 , LAST_NAME ),
80+ }) {
81+ transaction .run (" MERGE (address:Address{city: $city})"
82+ + "CREATE (:Person{firstName: $firstName, lastName: $lastName})"
83+ + "-[:LIVES_AT]-> (address)" ,
84+ Values .parameters ("firstName" , person .getKey (), "lastName" , person .getValue (), "city" , CITY ));
85+ }
6986
7087 transaction .commit ();
7188 transaction .close ();
@@ -74,16 +91,14 @@ void setup() {
7491
7592 @ Test
7693 void loadNamesOnlyProjection (@ Autowired ProjectionPersonRepository repository ) {
77- Collection <NamesOnly > people = repository .findByLastName (LAST_NAME );
78- assertThat (people ).hasSize (1 );
7994
80- NamesOnly person = people .iterator ().next ();
81- assertThat (person .getFirstName ()).isEqualTo (FIRST_NAME );
82- assertThat (person .getLastName ()).isEqualTo (LAST_NAME );
95+ Collection <NamesOnly > people = repository .findByLastName (LAST_NAME );
96+ assertThat (people ).hasSize (2 );
8397
84- String expectedFullName = FIRST_NAME + " " + LAST_NAME ;
85- assertThat (person . getFullName ()). isEqualTo ( expectedFullName );
98+ assertThat ( people ). extracting ( NamesOnly :: getFirstName ). containsExactlyInAnyOrder ( FIRST_NAME , FIRST_NAME2 ) ;
99+ assertThat (people ). extracting ( NamesOnly :: getLastName ). containsOnly ( LAST_NAME );
86100
101+ assertThat (people ).extracting (NamesOnly ::getFullName ).containsExactlyInAnyOrder (FIRST_NAME + " " + LAST_NAME , FIRST_NAME2 + " " + LAST_NAME );
87102 }
88103
89104 @ Test
@@ -153,10 +168,34 @@ void findDynamicProjectionForNamesOnlyDto(@Autowired ProjectionPersonRepository
153168
154169 }
155170
171+ @ Test // GH-2139
172+ void projectionsShouldBePaginatable (@ Autowired ProjectionPersonRepository repository ) {
173+
174+ Page <NamesOnly > people = repository .findAllProjectedBy (PageRequest .of (1 , 1 , Sort .by ("firstName" ).descending ()));
175+ assertThat (people .hasPrevious ()).isTrue ();
176+ assertThat (people .hasNext ()).isFalse ();
177+ assertThat (people ).hasSize (1 );
178+ assertThat (people ).extracting (NamesOnly ::getFullName ).containsExactly (FIRST_NAME + " " + LAST_NAME );
179+ }
180+
181+ @ Test // GH-2139
182+ void projectionsShouldBeSliceable (@ Autowired ProjectionPersonRepository repository ) {
183+
184+ Slice <NamesOnly > people = repository .findSliceProjectedBy (PageRequest .of (1 , 1 , Sort .by ("firstName" ).descending ()));
185+ assertThat (people .hasPrevious ()).isTrue ();
186+ assertThat (people .hasNext ()).isFalse ();
187+ assertThat (people ).hasSize (1 );
188+ assertThat (people ).extracting (NamesOnly ::getFullName ).containsExactly (FIRST_NAME + " " + LAST_NAME );
189+ }
190+
156191 interface ProjectionPersonRepository extends Neo4jRepository <Person , Long > {
157192
158193 Collection <NamesOnly > findByLastName (String lastName );
159194
195+ Page <NamesOnly > findAllProjectedBy (Pageable pageable );
196+
197+ Slice <NamesOnly > findSliceProjectedBy (Pageable pageable );
198+
160199 Collection <PersonSummary > findByFirstName (String firstName );
161200
162201 Collection <NamesOnlyDto > findByFirstNameAndLastName (String firstName , String lastName );
@@ -175,5 +214,4 @@ public Driver driver() {
175214 }
176215
177216 }
178-
179217}
0 commit comments