From d7a52ab7bd98cdce48feb98e0e9a7d9498543c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulf=20H=C3=A5g=C3=A5rd?= Date: Wed, 17 Jan 2024 16:36:50 +0100 Subject: [PATCH 1/3] SER-13233 Add support for sorting on multiple columns using CollectionOptions sortby. --- .../parts/CollectionOptionsQueryPart.java | 28 +++++++++++++++---- .../db/CollectionOptionsTest.java | 17 +++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java index 45afef38..c18d7b11 100644 --- a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java +++ b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java @@ -8,6 +8,10 @@ import java.lang.reflect.Method; import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.google.common.collect.Iterables.indexOf; import static java.util.Arrays.asList; @@ -17,6 +21,7 @@ public class CollectionOptionsQueryPart implements QueryPart { private final int collectionOptionsArgIndex; private final Query queryAnnotation; private final boolean isMono; + private static final Pattern SORT_BY_PART = Pattern.compile("(?[a-zåäö0-9_]+)(\\s+(?asc|desc))?"); public CollectionOptionsQueryPart(Method method) { collectionOptionsArgIndex = indexOf(asList(method.getParameterTypes()), CollectionOptions.class::isAssignableFrom); @@ -36,11 +41,24 @@ public void visit(StringBuilder sql, Object[] args) { if (collectionOptions != null) { if (collectionOptions.getSortBy() != null) { String sortBy = CamelSnakeConverter.camelToSnake(collectionOptions.getSortBy()); - for (String allowed : queryAnnotation.allowedSortColumns()) { - if (allowed.equals(sortBy)) { - collectionOptionsHasOrderBy = true; - addOrderBy(sql, buildOrderBy(collectionOptions.getOrder(), allowed)); - break; + var sortByParts = sortBy.split(","); + Collections.reverse(Arrays.asList(sortByParts)); + for (var sortByPart: sortByParts) { + Matcher matcher = SORT_BY_PART.matcher(sortByPart); + if (matcher.find()) { + var sortByColumn = matcher.group("sortby"); + if (sortByColumn != null && !sortByColumn.isBlank()) { + var order = matcher.group("order"); + if (order != null || collectionOptions.getSortBy() != null) { + for (String allowed : queryAnnotation.allowedSortColumns()) { + if (allowed.equals(sortByColumn)) { + collectionOptionsHasOrderBy = true; + addOrderBy(sql, buildOrderBy(order == null ? collectionOptions.getOrder() : CollectionOptions.SortOrder.valueOf(order.toUpperCase()), allowed)); + break; + } + } + } + } } } } diff --git a/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java b/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java index 789f1744..13387516 100644 --- a/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java +++ b/dao/src/test/java/se/fortnox/reactivewizard/db/CollectionOptionsTest.java @@ -170,6 +170,20 @@ void shouldAddDefaultSortBeforeQueryOrderBy() throws SQLException { mockDb.verifySelect("select * from table order by text desc, id LIMIT 101"); } + @Test + void shouldAddMultipleSort() throws SQLException { + CollectionOptions collectionOptions = new CollectionOptions("name asc, id desc", CollectionOptions.SortOrder.ASC); + collectionOptionsDao.selectWithDefaultSortingAllowMultiple(collectionOptions).blockFirst(); + mockDb.verifySelect("select * from table order by name ASC, id DESC, id LIMIT 101"); + } + + @Test + void shouldFallbackToCollectionOptionSortOrderMultipleSort() throws SQLException { + CollectionOptions collectionOptions = new CollectionOptions("name, id", CollectionOptions.SortOrder.DESC); + collectionOptionsDao.selectWithDefaultSortingAllowMultiple(collectionOptions).blockFirst(); + mockDb.verifySelect("select * from table order by name DESC, id DESC, id LIMIT 101"); + } + @Test void shouldAddOrderByBeforeQueryOrderByWithoutDefaultSort() throws Exception { CollectionOptions collectionOptions = new CollectionOptions("name", CollectionOptions.SortOrder.ASC); @@ -201,6 +215,9 @@ interface CollectionOptionsDao { @Query(value = "select * from table order by id", allowedSortColumns = {"name"}) Flux selectWithDefaultSorting(CollectionOptions collectionOptions); + @Query(value = "select * from table order by id", allowedSortColumns = {"name","id"}) + Flux selectWithDefaultSortingAllowMultiple(CollectionOptions collectionOptions); + @Query(value = "select * from table order by id", allowedSortColumns = {"name"}, defaultSort = "text desc") Flux selectWithDefaultSortingInQueryAndOptions(CollectionOptions collectionOptions); From 6e8b32884a9396de805b28a978627da09f6436cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulf=20H=C3=A5g=C3=A5rd?= Date: Fri, 19 Jan 2024 11:24:10 +0100 Subject: [PATCH 2/3] SER-13233 Add support for sorting on multiple columns using CollectionOptions sortby. Review changes. --- .../parts/CollectionOptionsQueryPart.java | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java index c18d7b11..7720f144 100644 --- a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java +++ b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java @@ -42,19 +42,29 @@ public void visit(StringBuilder sql, Object[] args) { if (collectionOptions.getSortBy() != null) { String sortBy = CamelSnakeConverter.camelToSnake(collectionOptions.getSortBy()); var sortByParts = sortBy.split(","); - Collections.reverse(Arrays.asList(sortByParts)); - for (var sortByPart: sortByParts) { - Matcher matcher = SORT_BY_PART.matcher(sortByPart); - if (matcher.find()) { - var sortByColumn = matcher.group("sortby"); - if (sortByColumn != null && !sortByColumn.isBlank()) { - var order = matcher.group("order"); - if (order != null || collectionOptions.getSortBy() != null) { - for (String allowed : queryAnnotation.allowedSortColumns()) { - if (allowed.equals(sortByColumn)) { - collectionOptionsHasOrderBy = true; - addOrderBy(sql, buildOrderBy(order == null ? collectionOptions.getOrder() : CollectionOptions.SortOrder.valueOf(order.toUpperCase()), allowed)); - break; + if (sortByParts.length == 1) { + for (String allowed : queryAnnotation.allowedSortColumns()) { + if (allowed.equals(sortBy)) { + collectionOptionsHasOrderBy = true; + addOrderBy(sql, buildOrderBy(collectionOptions.getOrder(), allowed)); + break; + } + } + } else { + Collections.reverse(Arrays.asList(sortByParts)); + for (var sortByPart: sortByParts) { + Matcher matcher = SORT_BY_PART.matcher(sortByPart); + if (matcher.find()) { + var sortByColumn = matcher.group("sortby"); + if (sortByColumn != null && !sortByColumn.isBlank()) { + var order = matcher.group("order"); + if (order != null || collectionOptions.getSortBy() != null) { + for (String allowed : queryAnnotation.allowedSortColumns()) { + if (allowed.equals(sortByColumn)) { + collectionOptionsHasOrderBy = true; + addOrderBy(sql, buildOrderBy(order == null ? collectionOptions.getOrder() : CollectionOptions.SortOrder.valueOf(order.toUpperCase()), allowed)); + break; + } } } } From 188ec2e2d236b952d86c73af24082bd542a6494a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulf=20H=C3=A5g=C3=A5rd?= Date: Sun, 21 Jan 2024 11:46:53 +0100 Subject: [PATCH 3/3] SER-13233 Add support for sorting on multiple columns using CollectionOptions sortby. Some improvements. --- .../query/parts/CollectionOptionsQueryPart.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java index 7720f144..7eed2b50 100644 --- a/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java +++ b/dao/src/main/java/se/fortnox/reactivewizard/db/query/parts/CollectionOptionsQueryPart.java @@ -56,15 +56,14 @@ public void visit(StringBuilder sql, Object[] args) { Matcher matcher = SORT_BY_PART.matcher(sortByPart); if (matcher.find()) { var sortByColumn = matcher.group("sortby"); - if (sortByColumn != null && !sortByColumn.isBlank()) { + if (sortByColumn != null) { var order = matcher.group("order"); - if (order != null || collectionOptions.getSortBy() != null) { - for (String allowed : queryAnnotation.allowedSortColumns()) { - if (allowed.equals(sortByColumn)) { - collectionOptionsHasOrderBy = true; - addOrderBy(sql, buildOrderBy(order == null ? collectionOptions.getOrder() : CollectionOptions.SortOrder.valueOf(order.toUpperCase()), allowed)); - break; - } + for (String allowed : queryAnnotation.allowedSortColumns()) { + if (allowed.equals(sortByColumn)) { + collectionOptionsHasOrderBy = true; + var sortOrder = order != null ? CollectionOptions.SortOrder.valueOf(order.toUpperCase()) : collectionOptions.getOrder(); + addOrderBy(sql, buildOrderBy(sortOrder, allowed)); + break; } } }