Skip to content

Commit 8b76138

Browse files
committed
DATAGRAPH-653 - Support sort on custom @querys.
1 parent 64b6791 commit 8b76138

File tree

6 files changed

+130
-16
lines changed

6 files changed

+130
-16
lines changed

spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/GraphRepositoryQuery.java

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.data.domain.PageImpl;
2525
import org.springframework.data.domain.Pageable;
2626
import org.springframework.data.domain.SliceImpl;
27+
import org.springframework.data.domain.Sort;
2728
import org.springframework.data.repository.query.Parameter;
2829
import org.springframework.data.repository.query.ParameterAccessor;
2930
import org.springframework.data.repository.query.Parameters;
@@ -45,9 +46,10 @@ public class GraphRepositoryQuery implements RepositoryQuery {
4546

4647
protected final Session session;
4748

48-
private final String SKIP = "sdnSkip";
49-
private final String LIMIT = "sdnLimit";
50-
private final String SKIP_LIMIT = " SKIP {" + SKIP + "} LIMIT {" + LIMIT + "}";
49+
private static final String SKIP = "sdnSkip";
50+
private static final String LIMIT = "sdnLimit";
51+
private static final String SKIP_LIMIT = " SKIP {" + SKIP + "} LIMIT {" + LIMIT + "}";
52+
private static final String ORDER_BY_CLAUSE = " ORDER BY %s";
5153

5254
public GraphRepositoryQuery(GraphQueryMethod graphQueryMethod, Session session) {
5355
this.graphQueryMethod = graphQueryMethod;
@@ -63,13 +65,22 @@ public final Object execute(Object[] parameters) {
6365

6466
ParameterAccessor accessor = new ParametersParameterAccessor(graphQueryMethod.getParameters(), parameters);
6567
ResultProcessor processor = graphQueryMethod.getResultProcessor();
66-
Object result = execute(returnType, concreteType, getQueryString(), params, accessor.getPageable());
68+
Object result = execute(returnType, concreteType, getQueryString(), params, accessor);
6769

6870
return Result.class.equals(returnType) ? result :
6971
processor.withDynamicProjection(accessor).processResult(result);
7072
}
7173

72-
protected Object execute(Class<?> returnType, Class<?> concreteType, String cypherQuery, Map<String, Object> queryParams, Pageable pageable) {
74+
protected Object execute(Class<?> returnType, Class<?> concreteType, String cypherQuery, Map<String, Object> queryParams, ParameterAccessor parameterAccessor) {
75+
Pageable pageable = parameterAccessor.getPageable();
76+
Sort sort = parameterAccessor.getSort();
77+
if (pageable!= null && pageable.getSort() != null) {
78+
sort = pageable.getSort();
79+
}
80+
if (sort != null) {
81+
//Custom queries in the OGM do not support pageable
82+
cypherQuery = addSorting(cypherQuery,sort);
83+
}
7384

7485
if (returnType.equals(Void.class) || returnType.equals(void.class)) {
7586
session.query(cypherQuery, queryParams);
@@ -179,4 +190,27 @@ private Long computeCount(Map<String, Object> params) {
179190
return null;
180191
}
181192

193+
private String addSorting(String baseQuery, Sort sort) {
194+
if (sort==null)
195+
{
196+
return baseQuery;
197+
}
198+
final String sortOrder = getSortOrder(sort);
199+
if (sortOrder.isEmpty()) {
200+
return baseQuery;
201+
}
202+
return baseQuery + String.format(ORDER_BY_CLAUSE, sortOrder);
203+
}
204+
205+
private String getSortOrder(Sort sort) {
206+
String result = "";
207+
for (Sort.Order order : sort) {
208+
if (!result.isEmpty()) {
209+
result += ", ";
210+
}
211+
result += order.getProperty() + " " + order.getDirection();
212+
}
213+
return result;
214+
}
215+
182216
}

spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/QueryResultGraphRepositoryQuery.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.neo4j.ogm.session.GraphCallback;
2828
import org.neo4j.ogm.session.Session;
2929
import org.neo4j.ogm.transaction.Transaction;
30-
import org.springframework.data.domain.Pageable;
30+
import org.springframework.data.repository.query.ParameterAccessor;
3131

3232
/**
3333
* Specialisation of {@link GraphRepositoryQuery} that handles mapping to objects annotated with <code>&#064;QueryResult</code>.
@@ -48,7 +48,7 @@ public QueryResultGraphRepositoryQuery(GraphQueryMethod graphQueryMethod, Sessio
4848
}
4949

5050
@Override
51-
protected Object execute(Class<?> returnType, final Class<?> concreteReturnType, String cypherQuery, Map<String, Object> queryParams, Pageable pageable) {
51+
protected Object execute(Class<?> returnType, final Class<?> concreteReturnType, String cypherQuery, Map<String, Object> queryParams, ParameterAccessor parameterAccessor) {
5252
Collection<Object> resultObjects = concreteReturnType.isInterface()
5353
? mapToProxy(concreteReturnType, cypherQuery, queryParams)
5454
: mapToConcreteType(concreteReturnType, cypherQuery, queryParams);

spring-data-neo4j/src/test/java/org/springframework/data/neo4j/examples/movies/repo/CinemaRepository.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public interface CinemaRepository extends GraphRepository<Cinema> {
7272

7373
Cinema findByName(String name, @Depth int depth);
7474

75-
@Query("MATCH (n:Theatre) RETURN n ORDER BY n.name")
75+
@Query("MATCH (n:Theatre) RETURN n")
7676
Page<Cinema> getPagedCinemasByName(Pageable pageable);
7777

7878
@Query(value = "MATCH (n:Theatre) RETURN n ORDER BY n.name", countQuery = "MATCH (n:Theatre) return count(*)")
@@ -81,7 +81,7 @@ public interface CinemaRepository extends GraphRepository<Cinema> {
8181
@Query(value = "MATCH (n:Theatre {city:{city}}) RETURN n ORDER BY n.name", countQuery = "MATCH (n:Theatre {city:{city}}) return count(*)")
8282
Page<Cinema> getPagedCinemasByCityWithPageCount(@Param("city") String city, Pageable pageable);
8383

84-
@Query("MATCH (n:Theatre) RETURN n ORDER BY n.name")
84+
@Query("MATCH (n:Theatre) RETURN n")
8585
Slice<Cinema> getSlicedCinemasByName(Pageable pageable);
8686

8787
Page<Cinema> findByLocation(String city, Pageable pageable);
@@ -94,4 +94,6 @@ public interface CinemaRepository extends GraphRepository<Cinema> {
9494

9595
List<Cinema> findByCapacity(int capacity, Pageable pageable);
9696

97+
@Query("MATCH (n:Theatre) RETURN n")
98+
List<Cinema> getCinemasSortedByName(Sort sort);
9799
}

spring-data-neo4j/src/test/java/org/springframework/data/neo4j/queries/PagedQueryTest.java

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ public void shouldFindSortedCinemas() {
300300
* @see DATAGRAPH-887
301301
*/
302302
@Test
303-
public void shouldFindPagedAndSortedCinemasByCapavity() {
303+
public void shouldFindPagedAndSortedCinemasByCapacity() {
304304
Pageable pageable = new PageRequest(0,4, Sort.Direction.ASC, "name");
305305

306306
List<Cinema> page = cinemaRepository.findByCapacity(500, pageable);
@@ -325,4 +325,87 @@ public void shouldFindPagedAndSortedCinemasByCapavity() {
325325
assertEquals("Ritzy", page.get(1).getName());
326326
}
327327

328+
/**
329+
* @see DATAGRAPH-653
330+
*/
331+
@Test
332+
public void shouldFindPagedCinemasSortedWithCustomQuery() {
333+
Pageable pageable = new PageRequest(0,4, Sort.Direction.ASC, "n.name");
334+
335+
Page<Cinema> page = cinemaRepository.getPagedCinemasByName(pageable);
336+
assertEquals(4, page.getNumberOfElements());
337+
assertTrue(page.hasNext());
338+
assertEquals(8, page.getTotalElements()); //this should not be relied on as incorrect as the total elements is an estimate
339+
assertEquals("Cineplex", page.getContent().get(0).getName());
340+
assertEquals("Inox", page.getContent().get(1).getName());
341+
assertEquals("Landmark", page.getContent().get(2).getName());
342+
assertEquals("Metro", page.getContent().get(3).getName());
343+
344+
page = cinemaRepository.getPagedCinemasByName(page.nextPageable());
345+
assertEquals(4, page.getNumberOfElements());
346+
assertTrue(page.hasNext());
347+
assertEquals(12, page.getTotalElements()); //this should not be relied on as incorrect as the total elements is an estimate
348+
assertEquals("Movietime", page.getContent().get(0).getName());
349+
assertEquals("PVR", page.getContent().get(1).getName());
350+
assertEquals("Picturehouse", page.getContent().get(2).getName());
351+
assertEquals("Rainbow", page.getContent().get(3).getName());
352+
353+
page = cinemaRepository.getPagedCinemasByName(page.nextPageable());
354+
assertEquals(2, page.getNumberOfElements());
355+
assertFalse(page.hasNext());
356+
assertEquals(10, page.getTotalElements()); //this should not be relied on as incorrect as the total elements is an estimate
357+
assertEquals("Regal", page.getContent().get(0).getName());
358+
assertEquals("Ritzy", page.getContent().get(1).getName());
359+
}
360+
361+
/**
362+
* @see DATAGRAPH-653
363+
*/
364+
@Test
365+
public void shouldFindSlicedCinemasSortedWithCustomQuery() {
366+
Pageable pageable = new PageRequest(0,4, Sort.Direction.ASC, "n.name");
367+
368+
Slice<Cinema> slice = cinemaRepository.getSlicedCinemasByName(pageable);
369+
assertEquals(4, slice.getNumberOfElements());
370+
assertTrue(slice.hasNext());
371+
assertEquals("Cineplex", slice.getContent().get(0).getName());
372+
assertEquals("Inox", slice.getContent().get(1).getName());
373+
assertEquals("Landmark", slice.getContent().get(2).getName());
374+
assertEquals("Metro", slice.getContent().get(3).getName());
375+
376+
slice = cinemaRepository.getSlicedCinemasByName(slice.nextPageable());
377+
assertEquals(4, slice.getNumberOfElements());
378+
assertTrue(slice.hasNext());
379+
assertEquals("Movietime", slice.getContent().get(0).getName());
380+
assertEquals("PVR", slice.getContent().get(1).getName());
381+
assertEquals("Picturehouse", slice.getContent().get(2).getName());
382+
assertEquals("Rainbow", slice.getContent().get(3).getName());
383+
384+
slice = cinemaRepository.getSlicedCinemasByName(slice.nextPageable());
385+
assertEquals(2, slice.getNumberOfElements());
386+
assertFalse(slice.hasNext());
387+
assertEquals("Regal", slice.getContent().get(0).getName());
388+
assertEquals("Ritzy", slice.getContent().get(1).getName());
389+
}
390+
391+
/**
392+
* @see DATAGRAPH-653
393+
*/
394+
@Test
395+
public void shouldFindCinemasSortedByNameWithCustomQuery() {
396+
Sort sort = new Sort(Sort.Direction.ASC, "n.name");
397+
List<Cinema> cinemas = cinemaRepository.getCinemasSortedByName(sort);
398+
assertEquals(10, cinemas.size());
399+
assertEquals("Cineplex", cinemas.get(0).getName());
400+
assertEquals("Inox", cinemas.get(1).getName());
401+
assertEquals("Landmark", cinemas.get(2).getName());
402+
assertEquals("Metro", cinemas.get(3).getName());
403+
assertEquals("Movietime", cinemas.get(4).getName());
404+
assertEquals("PVR", cinemas.get(5).getName());
405+
assertEquals("Picturehouse", cinemas.get(6).getName());
406+
assertEquals("Rainbow", cinemas.get(7).getName());
407+
assertEquals("Regal", cinemas.get(8).getName());
408+
assertEquals("Ritzy", cinemas.get(9).getName());
409+
}
410+
328411
}

src/main/asciidoc/reference/programming-model/attachdetach.adoc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,6 @@ Since the `movie.topActor` field has not been modified, it will not cascade the
109109
= Sorting and Paging
110110
Spring Data Neo4j supports sorting and paging of results when using Spring Data's `Pageable` and `Sort` interfaces.
111111

112-
[NOTE]
113-
====
114-
Spring Data Neo4j 4 does not yet support sorting on custom queries.
115-
====
116-
117112
====
118113
.Repository-based paging
119114
[source,java]

src/main/asciidoc/reference/programming-model/repositories.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ This query is executed separately after the result query and its result is used
6666

6767
[NOTE]
6868
====
69-
In the current version, custom queries do not support sorting or a custom depth.
69+
Custom queries do not support a custom depth.
7070
Additionally, `@Query` does not support mapping a path to domain entities, as such, a path should not be returned from a Cypher query.
7171
Instead, return nodes and relationships to have them mapped to domain entities.
7272
====

0 commit comments

Comments
 (0)