DATAJPA-1061 - Sorting now works with field aliases.

Extract field aliases in QueryUtils and add them in the set of possible sort criteria.

Original pull request: #276.
This commit is contained in:
GDR
2018-05-17 11:25:11 +02:00
committed by Jens Schauder
parent b538591237
commit a83c0bf2a5
2 changed files with 60 additions and 4 deletions

View File

@@ -77,6 +77,7 @@ import org.springframework.util.StringUtils;
* @author Nils Borrmann
* @author Reda.Housni-Alaoui
* @author Florian Lüdiger
* @author Grégoire Druant
*/
public abstract class QueryUtils {
@@ -121,6 +122,7 @@ public abstract class QueryUtils {
private static final Pattern PUNCTATION_PATTERN = Pattern.compile(".*((?![\\._])[\\p{Punct}|\\s])");
private static final Pattern FUNCTION_PATTERN;
private static final Pattern FIELD_ALIAS_PATTERN;
private static final String UNSAFE_PROPERTY_REFERENCE = "Sort expression '%s' must only contain property references or "
+ "aliases used in the select clause. If you really want to use something other than that for sorting, please use "
@@ -177,6 +179,14 @@ public abstract class QueryUtils {
builder.append("\\s+[as|AS]+\\s+(([\\w\\.]+))");
FUNCTION_PATTERN = compile(builder.toString());
builder = new StringBuilder();
builder.append("\\s+"); // at least one space
builder.append("[^\\s\\(\\)]+"); // No white char no bracket
builder.append("\\s+[as|AS]+\\s+(([\\w\\.]+))"); // the potential alias
FIELD_ALIAS_PATTERN = compile(builder.toString());
}
/**
@@ -253,10 +263,11 @@ public abstract class QueryUtils {
}
Set<String> aliases = getOuterJoinAliases(query);
Set<String> functionAliases = getFunctionAliases(query);
Set<String> fieldAliases = getFunctionAliases(query);
fieldAliases.addAll(getFieldAliases(query));
for (Order order : sort) {
builder.append(getOrderClause(aliases, functionAliases, alias, order)).append(", ");
builder.append(getOrderClause(aliases, fieldAliases, alias, order)).append(", ");
}
builder.delete(builder.length() - 2, builder.length());
@@ -273,14 +284,14 @@ public abstract class QueryUtils {
* @param order the order object to build the clause for. Must not be {@literal null}.
* @return a String containing a order clause. Guaranteed to be not {@literal null}.
*/
private static String getOrderClause(Set<String> joinAliases, Set<String> functionAlias, @Nullable String alias,
private static String getOrderClause(Set<String> joinAliases, Set<String> fieldAlias, @Nullable String alias,
Order order) {
String property = order.getProperty();
checkSortExpression(order);
if (functionAlias.contains(property)) {
if (fieldAlias.contains(property)) {
return String.format("%s %s", property, toJpaDirection(order));
}
@@ -322,6 +333,26 @@ public abstract class QueryUtils {
return result;
}
/**
* Returns the aliases used for fields in the query.
*
* @param query a {@literal String} containing a query. Must not be {@literal null}.
* @return a {@literal Set} containing all found aliases. Guaranteed to be not {@literal null}.
*/
private static Set<String> getFieldAliases(String query) {
Set<String> result = new HashSet<>();
Matcher matcher = FIELD_ALIAS_PATTERN.matcher(query);
while (matcher.find()) {
String alias = matcher.group(1);
if (StringUtils.hasText(alias)) {
result.add(alias);
}
}
return result;
}
/**
* Returns the aliases used for aggregate functions like {@code SUM, COUNT, ...}.
*

View File

@@ -39,6 +39,7 @@ import org.springframework.data.jpa.domain.JpaSort;
* @author Christoph Strobl
* @author Jens Schauder
* @author Florian Lüdiger
* @author Grégoire Druant
*/
public class QueryUtilsUnitTests {
@@ -423,6 +424,30 @@ public class QueryUtilsUnitTests {
" where user.age = 18\n "));
}
@Test // DATAJPA-1061
public void appliesSortCorrectlyForFieldAliases() {
String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a";
Sort sort = Sort.by("authorName");
String fullQuery = applySorting(query, sort);
assertThat(fullQuery, endsWith("order by authorName asc"));
}
@Test // DATAJPA-1061
public void appliesSortCorrectlyForFunctionAliases() {
String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a";
Sort sort = Sort.by("title");
String fullQuery = applySorting(query, sort);
assertThat(fullQuery, endsWith("order by title asc"));
}
@Test // DATAJPA-1061
public void appliesSortCorrectlyForSimpleField() {
String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a";
Sort sort = Sort.by("price");
String fullQuery = applySorting(query, sort);
assertThat(fullQuery, endsWith("order by m.price asc"));
}
private static void assertCountQuery(String originalQuery, String countQuery) {
assertThat(createCountQueryFor(originalQuery), is(countQuery));
}