Skip to content

Commit 9625a81

Browse files
babltigamikereiche
authored andcommitted
added QueryDSL sorting support (#1653)
closes #1363
1 parent 4b92898 commit 9625a81

File tree

5 files changed

+91
-10
lines changed

5 files changed

+91
-10
lines changed

src/main/java/com/querydsl/couchbase/document/CouchbaseDocumentSerializer.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@
3939
import com.querydsl.core.types.SubQueryExpression;
4040
import com.querydsl.core.types.TemplateExpression;
4141
import com.querydsl.core.types.Visitor;
42+
import com.querydsl.core.types.Order;
4243

4344
/**
4445
* Serializes the given Querydsl query to a Document query for Couchbase.
4546
*
4647
* @author Michael Reiche
48+
* @author Tigran Babloyan
4749
*/
4850
public abstract class CouchbaseDocumentSerializer implements Visitor<Object, Void> {
4951

@@ -55,8 +57,15 @@ public Sort toSort(List<OrderSpecifier<?>> orderBys) {
5557
Sort sort = Sort.unsorted();
5658
for (OrderSpecifier<?> orderBy : orderBys) {
5759
Object key = orderBy.getTarget().accept(this, null);
58-
// sort.and(Sort.by(orderBy));
59-
// sort.append(key.toString(), orderBy.getOrder() == Order.ASC ? 1 : -1);
60+
String keyAsString = key.toString();
61+
Sort.NullHandling sortNullHandling = switch (orderBy.getNullHandling()) {
62+
case NullsFirst -> Sort.NullHandling.NULLS_FIRST;
63+
case NullsLast -> Sort.NullHandling.NULLS_LAST;
64+
default -> Sort.NullHandling.NATIVE;
65+
};
66+
Sort.Direction sortDirection = orderBy.getOrder() == Order.ASC ? Sort.Direction.ASC : Sort.Direction.DESC;
67+
Sort.Order sortOrder = new Sort.Order(sortDirection, keyAsString, sortNullHandling);
68+
sort = sort.and(Sort.by(sortOrder));
6069
}
6170
return sort;
6271
}

src/main/java/org/springframework/data/couchbase/repository/support/BasicQuery.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* BasicQuery for Querydsl
3131
*
3232
* @author Michael Reiche
33+
* @author Tigran Babloyan
3334
*/
3435
public class BasicQuery extends Query {
3536

@@ -40,12 +41,11 @@ public class BasicQuery extends Query {
4041
* {@link CouchbaseDocument}.
4142
*
4243
* @param query must not be {@literal null}.
43-
* @param projectionFields must not be {@literal null}.
44+
* @param projectionFields can be {@literal null}.
4445
* @throws IllegalArgumentException when {@code sortObject} or {@code fieldsObject} is {@literal null}.
4546
*/
4647
public BasicQuery(Query query, Map<String, String> projectionFields) {
4748
super(query);
48-
Assert.notNull(projectionFields, "Field document must not be null");
4949
this.projectionFields = projectionFields;
5050
}
5151

src/main/java/org/springframework/data/couchbase/repository/support/SpringDataCouchbaseQuery.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.springframework.data.couchbase.core.CouchbaseOperations;
2828
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation;
29+
import org.springframework.data.couchbase.core.query.Query;
2930
import org.springframework.data.domain.Page;
3031
import org.springframework.data.domain.PageImpl;
3132
import org.springframework.data.domain.Pageable;
@@ -250,7 +251,7 @@ protected org.springframework.data.couchbase.core.query.Query createQuery(@Nulla
250251
@Nullable Expression<?> projection, QueryModifiers modifiers, List<OrderSpecifier<?>> orderBy) {
251252

252253
Map<String, String> fields = createProjection(projection);
253-
BasicQuery basicQuery = new BasicQuery(createCriteria(filter), fields);
254+
BasicQuery basicQuery = filter == null ? new BasicQuery(new Query(), fields) : new BasicQuery(createCriteria(filter), fields);
254255

255256
Integer limit = modifiers.getLimitAsInteger();
256257
Integer offset = modifiers.getOffsetAsInteger();

src/main/java/org/springframework/data/couchbase/repository/support/SpringDataCouchbaseQuerySupport.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
/**
3131
* @author Michael Reiche
32+
* @author Tigran Babloyan
3233
*/
3334
abstract class SpringDataCouchbaseQuerySupport<Q extends SpringDataCouchbaseQuerySupport<Q>>
3435
extends AbstractCouchbaseQueryDSL<Q> {
@@ -84,10 +85,9 @@ public String toString() {
8485
// sb.append(", ").append(projection.toJson(JSON_WRITER_SETTINGS, codec));
8586
// }
8687
sb.append(")");
87-
// TODO
88-
// if (!sort.isEmpty()) {
89-
// sb.append(".sort(").append(sort.toJson(JSON_WRITER_SETTINGS, codec)).append(")");
90-
// }
88+
if (!sort.isEmpty()) {
89+
sb.append(".sort(").append(sort).append(")");
90+
}
9191
if (getQueryMixin().getMetadata().getModifiers().getOffset() != null) {
9292
sb.append(".skip(").append(getQueryMixin().getMetadata().getModifiers().getOffset()).append(")");
9393
}
@@ -128,6 +128,6 @@ public CouchbaseDocument asDocument() {
128128
* CouchbaseDocumentSerializer#toSort(List)
129129
*/
130130
protected Sort createSort(List<OrderSpecifier<?>> orderSpecifiers) {
131-
return null; // TODO serializer.toSort(orderSpecifiers);
131+
return serializer.toSort(orderSpecifiers);
132132
}
133133
}

src/test/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQuerydslIntegrationTests.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
2020
import static org.junit.jupiter.api.Assertions.assertEquals;
2121
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
2223
import static org.springframework.data.couchbase.util.Util.comprises;
2324
import static org.springframework.data.couchbase.util.Util.exactly;
2425

2526
import java.util.Arrays;
27+
import java.util.Comparator;
2628
import java.util.Locale;
2729
import java.util.Optional;
30+
import java.util.stream.StreamSupport;
2831

2932
import org.junit.jupiter.api.AfterAll;
3033
import org.junit.jupiter.api.BeforeAll;
@@ -53,6 +56,7 @@
5356
import org.springframework.data.couchbase.util.ClusterType;
5457
import org.springframework.data.couchbase.util.IgnoreWhen;
5558
import org.springframework.data.couchbase.util.JavaIntegrationTests;
59+
import org.springframework.data.domain.Sort;
5660
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
5761
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
5862

@@ -67,6 +71,7 @@
6771
* Repository tests
6872
*
6973
* @author Michael Reiche
74+
* @author Tigran Babloyan
7075
*/
7176
@SpringJUnitConfig(CouchbaseRepositoryQuerydslIntegrationTests.Config.class)
7277
@IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED)
@@ -410,6 +415,72 @@ void testIn() {
410415
assertEquals(" WHERE name in $1", bq(predicate));
411416
}
412417
}
418+
419+
@Test
420+
void testSort(){
421+
{
422+
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
423+
Iterable<Airline> result = airlineRepository.findAll(predicate, Sort.by("name").ascending());
424+
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
425+
Arrays.stream(saved)
426+
.sorted(Comparator.comparing(Airline::getName))
427+
.toArray(Airline[]::new),
428+
"Order of airlines does not match");
429+
}
430+
431+
{
432+
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
433+
Iterable<Airline> result = airlineRepository.findAll(predicate, Sort.by("name").descending());
434+
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
435+
Arrays.stream(saved)
436+
.sorted(Comparator.comparing(Airline::getName).reversed())
437+
.toArray(Airline[]::new),
438+
"Order of airlines does not match");
439+
}
440+
441+
{
442+
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
443+
Iterable<Airline> result = airlineRepository.findAll(predicate, airline.name.asc());
444+
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
445+
Arrays.stream(saved)
446+
.sorted(Comparator.comparing(Airline::getName))
447+
.toArray(Airline[]::new),
448+
"Order of airlines does not match");
449+
}
450+
451+
{
452+
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
453+
Iterable<Airline> result = airlineRepository.findAll(predicate, airline.name.desc());
454+
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
455+
Arrays.stream(saved)
456+
.sorted(Comparator.comparing(Airline::getName).reversed())
457+
.toArray(Airline[]::new),
458+
"Order of airlines does not match");
459+
}
460+
461+
{
462+
Comparator<String> nullSafeStringComparator = Comparator
463+
.nullsFirst(String::compareTo);
464+
Iterable<Airline> result = airlineRepository.findAll(airline.hqCountry.asc().nullsFirst());
465+
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
466+
Arrays.stream(saved)
467+
.sorted(Comparator.comparing(Airline::getHqCountry, nullSafeStringComparator))
468+
.toArray(Airline[]::new),
469+
"Order of airlines does not match");
470+
}
471+
472+
{
473+
Comparator<String> nullSafeStringComparator = Comparator
474+
.nullsFirst(String::compareTo);
475+
Iterable<Airline> result = airlineRepository.findAll(airline.hqCountry.desc().nullsLast());
476+
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
477+
Arrays.stream(saved)
478+
.sorted(Comparator.comparing(Airline::getHqCountry, nullSafeStringComparator).reversed())
479+
.toArray(Airline[]::new),
480+
"Order of airlines does not match");
481+
}
482+
}
483+
413484

414485
@Test
415486
void testNotIn() {

0 commit comments

Comments
 (0)