Skip to content

QueryDSL Sorting Support #1653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@
import com.querydsl.core.types.SubQueryExpression;
import com.querydsl.core.types.TemplateExpression;
import com.querydsl.core.types.Visitor;
import com.querydsl.core.types.Order;

/**
* Serializes the given Querydsl query to a Document query for Couchbase.
*
* @author Michael Reiche
* @author Tigran Babloyan
*/
public abstract class CouchbaseDocumentSerializer implements Visitor<Object, Void> {

Expand All @@ -55,8 +57,15 @@ public Sort toSort(List<OrderSpecifier<?>> orderBys) {
Sort sort = Sort.unsorted();
for (OrderSpecifier<?> orderBy : orderBys) {
Object key = orderBy.getTarget().accept(this, null);
// sort.and(Sort.by(orderBy));
// sort.append(key.toString(), orderBy.getOrder() == Order.ASC ? 1 : -1);
String keyAsString = key.toString();
Sort.NullHandling sortNullHandling = switch (orderBy.getNullHandling()) {
case NullsFirst -> Sort.NullHandling.NULLS_FIRST;
case NullsLast -> Sort.NullHandling.NULLS_LAST;
default -> Sort.NullHandling.NATIVE;
};
Sort.Direction sortDirection = orderBy.getOrder() == Order.ASC ? Sort.Direction.ASC : Sort.Direction.DESC;
Sort.Order sortOrder = new Sort.Order(sortDirection, keyAsString, sortNullHandling);
sort = sort.and(Sort.by(sortOrder));
}
return sort;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* BasicQuery for Querydsl
*
* @author Michael Reiche
* @author Tigran Babloyan
*/
public class BasicQuery extends Query {

Expand All @@ -40,12 +41,11 @@ public class BasicQuery extends Query {
* {@link CouchbaseDocument}.
*
* @param query must not be {@literal null}.
* @param projectionFields must not be {@literal null}.
* @param projectionFields can be {@literal null}.
* @throws IllegalArgumentException when {@code sortObject} or {@code fieldsObject} is {@literal null}.
*/
public BasicQuery(Query query, Map<String, String> projectionFields) {
super(query);
Assert.notNull(projectionFields, "Field document must not be null");
this.projectionFields = projectionFields;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -250,7 +251,7 @@ protected org.springframework.data.couchbase.core.query.Query createQuery(@Nulla
@Nullable Expression<?> projection, QueryModifiers modifiers, List<OrderSpecifier<?>> orderBy) {

Map<String, String> fields = createProjection(projection);
BasicQuery basicQuery = new BasicQuery(createCriteria(filter), fields);
BasicQuery basicQuery = filter == null ? new BasicQuery(new Query(), fields) : new BasicQuery(createCriteria(filter), fields);

Integer limit = modifiers.getLimitAsInteger();
Integer offset = modifiers.getOffsetAsInteger();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

/**
* @author Michael Reiche
* @author Tigran Babloyan
*/
abstract class SpringDataCouchbaseQuerySupport<Q extends SpringDataCouchbaseQuerySupport<Q>>
extends AbstractCouchbaseQueryDSL<Q> {
Expand Down Expand Up @@ -84,10 +85,9 @@ public String toString() {
// sb.append(", ").append(projection.toJson(JSON_WRITER_SETTINGS, codec));
// }
sb.append(")");
// TODO
// if (!sort.isEmpty()) {
// sb.append(".sort(").append(sort.toJson(JSON_WRITER_SETTINGS, codec)).append(")");
// }
if (!sort.isEmpty()) {
sb.append(".sort(").append(sort).append(")");
}
if (getQueryMixin().getMetadata().getModifiers().getOffset() != null) {
sb.append(".skip(").append(getQueryMixin().getMetadata().getModifiers().getOffset()).append(")");
}
Expand Down Expand Up @@ -128,6 +128,6 @@ public CouchbaseDocument asDocument() {
* CouchbaseDocumentSerializer#toSort(List)
*/
protected Sort createSort(List<OrderSpecifier<?>> orderSpecifiers) {
return null; // TODO serializer.toSort(orderSpecifiers);
return serializer.toSort(orderSpecifiers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.springframework.data.couchbase.util.Util.comprises;
import static org.springframework.data.couchbase.util.Util.exactly;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.StreamSupport;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -53,6 +56,7 @@
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.data.couchbase.util.JavaIntegrationTests;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

Expand All @@ -67,6 +71,7 @@
* Repository tests
*
* @author Michael Reiche
* @author Tigran Babloyan
*/
@SpringJUnitConfig(CouchbaseRepositoryQuerydslIntegrationTests.Config.class)
@IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED)
Expand Down Expand Up @@ -410,6 +415,72 @@ void testIn() {
assertEquals(" WHERE name in $1", bq(predicate));
}
}

@Test
void testSort(){
{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
Iterable<Airline> result = airlineRepository.findAll(predicate, Sort.by("name").ascending());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(Airline::getName))
.toArray(Airline[]::new),
"Order of airlines does not match");
}

{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
Iterable<Airline> result = airlineRepository.findAll(predicate, Sort.by("name").descending());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(Airline::getName).reversed())
.toArray(Airline[]::new),
"Order of airlines does not match");
}

{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
Iterable<Airline> result = airlineRepository.findAll(predicate, airline.name.asc());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(Airline::getName))
.toArray(Airline[]::new),
"Order of airlines does not match");
}

{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(Airline::getName).toList());
Iterable<Airline> result = airlineRepository.findAll(predicate, airline.name.desc());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(Airline::getName).reversed())
.toArray(Airline[]::new),
"Order of airlines does not match");
}

{
Comparator<String> nullSafeStringComparator = Comparator
.nullsFirst(String::compareTo);
Iterable<Airline> result = airlineRepository.findAll(airline.hqCountry.asc().nullsFirst());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(Airline::getHqCountry, nullSafeStringComparator))
.toArray(Airline[]::new),
"Order of airlines does not match");
}

{
Comparator<String> nullSafeStringComparator = Comparator
.nullsFirst(String::compareTo);
Iterable<Airline> result = airlineRepository.findAll(airline.hqCountry.desc().nullsLast());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(Airline[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(Airline::getHqCountry, nullSafeStringComparator).reversed())
.toArray(Airline[]::new),
"Order of airlines does not match");
}
}


@Test
void testNotIn() {
Expand Down