Skip to content

Commit 2e33841

Browse files
mp911dechristophstrobl
authored andcommitted
Fix result sorting using Redis repository query methods.
We now correctly apply sorting when calling a repository query method with a Sort parameter or when defining the sort order as part of the method name. Closes: #2080 Original Pull Request: #2087
1 parent f69146c commit 2e33841

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

src/main/asciidoc/reference/redis-repositories.adoc

+20-1
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,6 @@ public interface PersonRepository extends CrudRepository<Person, String> {
673673
----
674674
====
675675

676-
677676
NOTE: Please make sure properties used in finder methods are set up for indexing.
678677

679678
NOTE: Query methods for Redis repositories support only queries for entities and collections of entities with paging.
@@ -711,6 +710,26 @@ The following table provides an overview of the keywords supported for Redis and
711710
|===============
712711
====
713712

713+
[[redis.repositories.queries.sort]]
714+
=== Sorting Query Method results
715+
716+
Redis repositories allow various approaches to define sorting order. Redis itself does not support in-flight sorting when retrieving hashes or sets. Therefore, Redis repository query methods construct a `Comparator` that is applied to the result before returning results as `List`. Let's take a look at the following example:
717+
718+
.Sorting Query Results
719+
====
720+
[source,java]
721+
----
722+
interface PersonRepository extends RedisRepository<Person, String> {
723+
724+
List<Person> findByFirstnameSortByAgeDesc(String firstname); <1>
725+
726+
List<Person> findByFirstname(String firstname, Sort sort); <2>
727+
}
728+
----
729+
<1> Static sorting derived from method name.
730+
<2> Dynamic sorting using a method argument.
731+
====
732+
714733
[[redis.repositories.cluster]]
715734
== Redis Repositories Running on a Cluster
716735

src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.data.keyvalue.core.CriteriaAccessor;
3030
import org.springframework.data.keyvalue.core.QueryEngine;
3131
import org.springframework.data.keyvalue.core.SortAccessor;
32+
import org.springframework.data.keyvalue.core.SpelSortAccessor;
3233
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
3334
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
3435
import org.springframework.data.redis.core.convert.GeoIndexedPropertyValue;
@@ -37,6 +38,7 @@
3738
import org.springframework.data.redis.repository.query.RedisOperationChain.NearPath;
3839
import org.springframework.data.redis.repository.query.RedisOperationChain.PathAndValue;
3940
import org.springframework.data.redis.util.ByteUtils;
41+
import org.springframework.expression.spel.standard.SpelExpressionParser;
4042
import org.springframework.lang.Nullable;
4143
import org.springframework.util.CollectionUtils;
4244

@@ -53,7 +55,7 @@ class RedisQueryEngine extends QueryEngine<RedisKeyValueAdapter, RedisOperationC
5355
* Creates new {@link RedisQueryEngine} with defaults.
5456
*/
5557
RedisQueryEngine() {
56-
this(new RedisCriteriaAccessor(), null);
58+
this(new RedisCriteriaAccessor(), new SpelSortAccessor(new SpelExpressionParser()));
5759
}
5860

5961
/**
@@ -76,6 +78,16 @@ private RedisQueryEngine(CriteriaAccessor<RedisOperationChain> criteriaAccessor,
7678
@SuppressWarnings("unchecked")
7779
public <T> Collection<T> execute(RedisOperationChain criteria, Comparator<?> sort, long offset, int rows,
7880
String keyspace, Class<T> type) {
81+
List<T> result = doFind(criteria, offset, rows, keyspace, type);
82+
83+
if (sort != null) {
84+
result.sort((Comparator<? super T>) sort);
85+
}
86+
87+
return result;
88+
}
89+
90+
private <T> List<T> doFind(RedisOperationChain criteria, long offset, int rows, String keyspace, Class<T> type) {
7991

8092
if (criteria == null
8193
|| (CollectionUtils.isEmpty(criteria.getOrSismember()) && CollectionUtils.isEmpty(criteria.getSismember()))

src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTestBase.java

+47-1
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919

2020
import lombok.Data;
2121
import lombok.Value;
22+
import lombok.With;
2223

2324
import java.util.Arrays;
2425
import java.util.Collections;
2526
import java.util.List;
2627
import java.util.Optional;
2728

28-
import lombok.With;
2929
import org.junit.jupiter.api.BeforeEach;
3030
import org.junit.jupiter.api.Test;
3131

@@ -36,6 +36,7 @@
3636
import org.springframework.data.domain.Page;
3737
import org.springframework.data.domain.PageRequest;
3838
import org.springframework.data.domain.Pageable;
39+
import org.springframework.data.domain.Sort;
3940
import org.springframework.data.geo.Distance;
4041
import org.springframework.data.geo.Metrics;
4142
import org.springframework.data.geo.Point;
@@ -117,6 +118,45 @@ void simpleFindByMultipleProperties() {
117118
assertThat(repo.findByFirstnameAndLastname("egwene", "al'vere").get(0)).isEqualTo(egwene);
118119
}
119120

121+
@Test // GH-2080
122+
void simpleFindAndSort() {
123+
124+
Person egwene = new Person();
125+
egwene.firstname = "egwene";
126+
egwene.lastname = "al'vere";
127+
128+
Person marin = new Person();
129+
marin.firstname = "marin";
130+
marin.lastname = "al'vere";
131+
132+
repo.saveAll(Arrays.asList(egwene, marin));
133+
134+
assertThat(repo.findByLastname("al'vere", Sort.by(Sort.Direction.ASC, "firstname"))).containsSequence(egwene,
135+
marin);
136+
assertThat(repo.findByLastname("al'vere", Sort.by(Sort.Direction.DESC, "firstname"))).containsSequence(marin,
137+
egwene);
138+
139+
assertThat(repo.findByLastnameOrderByFirstnameAsc("al'vere")).containsSequence(egwene, marin);
140+
assertThat(repo.findByLastnameOrderByFirstnameDesc("al'vere")).containsSequence(marin, egwene);
141+
}
142+
143+
@Test // GH-2080
144+
void simpleFindAllWithSort() {
145+
146+
Person egwene = new Person();
147+
egwene.firstname = "egwene";
148+
egwene.lastname = "al'vere";
149+
150+
Person marin = new Person();
151+
marin.firstname = "marin";
152+
marin.lastname = "al'vere";
153+
154+
repo.saveAll(Arrays.asList(egwene, marin));
155+
156+
assertThat(repo.findAll(Sort.by(Sort.Direction.ASC, "firstname"))).containsSequence(egwene, marin);
157+
assertThat(repo.findAll(Sort.by(Sort.Direction.DESC, "firstname"))).containsSequence(marin, egwene);
158+
}
159+
120160
@Test // DATAREDIS-425
121161
void findReturnsReferenceDataCorrectly() {
122162

@@ -441,6 +481,12 @@ public static interface PersonRepository
441481

442482
List<Person> findByLastname(String lastname);
443483

484+
List<Person> findByLastname(String lastname, Sort sort);
485+
486+
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
487+
488+
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
489+
444490
Page<Person> findPersonByLastname(String lastname, Pageable page);
445491

446492
List<Person> findPersonByAliveIsTrue();

0 commit comments

Comments
 (0)