Merge pull request #2108 from harshtuna/collections-order-nulls-last

fix for #2106 enable NullsLast support in querydsl-collections orderBy()
This commit is contained in:
Jan-Willem Gmelig Meyling 2021-01-01 17:14:05 +01:00 committed by GitHub
commit 3411f7857d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 37 additions and 14 deletions

View File

@ -13,6 +13,7 @@ A huge thanks goes out to all contributors that made this release possible in th
* **[@mp911de](https://github.com/mp911de)**, for working on the MongoDB Document API;
* **[@daniel-shuy](https://github.com/daniel-shuy)**, for working on decoupling `querydsl-sql` from `joda-time:joda-time`;
* **[@heesuk-ahn](https://github.com/heesuk-ahn)**, for working on improved Hibernate support and count query generation in `JPASQLQuery`;
* **[@harshtuna](https://github.com/harshtuna)**, for working on NullsLast ordering in `querydsl-collections`;
* **[@jwgmeligmeyling](https://github.com/jwgmeligmeyling)**, **[@Shredder121](https://github.com/Shredder121)**, **[@johnktims](https://github.com/johnktims)**, **[@idosal](https://github.com/idosal)** and **[@robertandrewbain](https://github.com/robertandrewbain)**.
#### New features
@ -34,6 +35,7 @@ A huge thanks goes out to all contributors that made this release possible in th
does not have to be available at runtime and is not a transitive dependency.
* [#658](https://github.com/querydsl/querydsl/issues/658) - Added `JPAExpressions#treat` which can be used to generate JPA 2.1 Treated path expressions.
* [#2666](https://github.com/querydsl/querydsl/issues/2666) - More descriptive error message when using unsupported query features in JPA.
* [#2106](https://github.com/querydsl/querydsl/issues/2106) - Support NullsLast ordering in `querydsl-collections`.
#### Bugfixes

View File

@ -214,13 +214,15 @@ public class DefaultQueryEngine implements QueryEngine {
List<OrderSpecifier<?>> orderBy = metadata.getOrderBy();
Expression<Object>[] orderByExpr = new Expression[orderBy.size()];
boolean[] directions = new boolean[orderBy.size()];
boolean[] nullsLast = new boolean[orderBy.size()];
for (int i = 0; i < orderBy.size(); i++) {
orderByExpr[i] = (Expression) orderBy.get(i).getTarget();
directions[i] = orderBy.get(i).getOrder() == Order.ASC;
nullsLast[i] = orderBy.get(i).getNullHandling() == OrderSpecifier.NullHandling.NullsLast;
}
Expression<?> expr = new ArrayConstructorExpression<Object>(Object[].class, orderByExpr);
Evaluator orderEvaluator = evaluatorFactory.create(metadata, sources, expr);
list.sort(new MultiComparator(orderEvaluator, directions));
list.sort(new MultiComparator(orderEvaluator, directions, nullsLast));
}
private List<?> project(QueryMetadata metadata, List<Expression<?>> sources, List<?> list) {

View File

@ -23,7 +23,6 @@ import com.querydsl.core.util.NullSafeComparableComparator;
* {@code MultiComparator} compares arrays
*
* @param <T> element type
*
* @author tiwe
*/
public class MultiComparator<T> implements Comparator<T>, Serializable {
@ -35,16 +34,19 @@ public class MultiComparator<T> implements Comparator<T>, Serializable {
private final boolean[] asc;
private final boolean[] nullsLast;
private final transient Evaluator<Object[]> ev;
public MultiComparator(Evaluator<Object[]> ev, boolean[] directions) {
public MultiComparator(Evaluator<Object[]> ev, boolean[] directions, boolean[] nullsLast) {
this.ev = ev;
this.asc = directions.clone();
this.nullsLast = nullsLast.clone();
}
@Override
public int compare(T o1, T o2) {
if (o1.getClass().isArray()) {
if (o1 instanceof Object[]) {
return innerCompare(ev.evaluate((Object[]) o1), ev.evaluate((Object[]) o2));
} else {
return innerCompare(ev.evaluate(o1), ev.evaluate(o2));
@ -53,16 +55,16 @@ public class MultiComparator<T> implements Comparator<T>, Serializable {
private int innerCompare(Object[] o1, Object[] o2) {
for (int i = 0; i < o1.length; i++) {
int res;
if (o1[i] == null) {
res = o2[i] == null ? 0 : -1;
return o2[i] == null ? 0 : nullsLast[i] ? 1 : -1;
} else if (o2[i] == null) {
res = 1;
return nullsLast[i] ? -1 : 1;
} else {
int res;
res = naturalOrder.compare(o1[i], o2[i]);
}
if (res != 0) {
return asc[i] ? res : -res;
if (res != 0) {
return asc[i] ? res : -res;
}
}
}
return 0;

View File

@ -34,7 +34,8 @@ public class MultiComparatorTest {
@Test
public void test() {
MultiComparator<Object[]> comparator = new MultiComparator<Object[]>(evaluator, new boolean[]{true, true});
MultiComparator<Object[]> comparator = new MultiComparator<Object[]>(evaluator, new boolean[]{true, true},
new boolean[]{true, true});
assertTrue(comparator.compare(new Object[]{"a", "b"}, new Object[]{"a","c"}) < 0);
assertTrue(comparator.compare(new Object[]{"b", "a"}, new Object[]{"a","b"}) > 0);
assertTrue(comparator.compare(new Object[]{"b", "b"}, new Object[]{"b","b"}) == 0);

View File

@ -60,9 +60,25 @@ public class OrderTest extends AbstractQueryTest {
@Test
public void with_null() {
List<Cat> cats = Arrays.asList(new Cat(), new Cat("Bob"));
assertEquals(cats, query().from(cat, cats).orderBy(cat.name.asc()).select(cat).fetch());
assertEquals(Arrays.asList(cats.get(1), cats.get(0)), query().from(cat, cats).orderBy(cat.name.desc()).select(cat).fetch());
Cat unknown = new Cat();
Cat bob = new Cat("Bob");
Cat alex = new Cat("Alex");
List<Cat> cats = Arrays.asList(alex, unknown, bob);
assertEquals(Arrays.asList(unknown, alex, bob),
query().from(cat, cats).orderBy(cat.name.asc()).select(cat).fetch());
assertEquals(Arrays.asList(unknown, bob, alex),
query().from(cat, cats).orderBy(cat.name.desc()).select(cat).fetch());
}
@Test
public void with_nulls_last() {
Cat unknown = new Cat();
Cat bob = new Cat("Bob");
Cat alex = new Cat("Alex");
List<Cat> cats = Arrays.asList(alex, unknown, bob);
assertEquals(Arrays.asList(bob, alex, unknown),
query().from(this.cat, cats).orderBy(this.cat.name.desc().nullsLast()).select(this.cat).fetch());
assertEquals(Arrays.asList(alex, bob, unknown),
query().from(this.cat, cats).orderBy(this.cat.name.asc().nullsLast()).select(this.cat).fetch());
}
}