added aggregate function to the sort columns (#4208)
* added aggregate function to the sort columns, as the sql will not work on other dbs other than H2 that use order by * rename parameter to make it more meaningful * extract parameter to a field Co-authored-by: Steven Li <steven@smilecdr.com>
This commit is contained in:
parent
0a160a35ba
commit
d8dd42ced5
|
@ -146,6 +146,8 @@ public class QueryStack {
|
|||
private final DaoConfig myDaoConfig;
|
||||
private final EnumSet<PredicateBuilderTypeEnum> myReusePredicateBuilderTypes;
|
||||
private Map<PredicateBuilderCacheKey, BaseJoiningPredicateBuilder> myJoinMap;
|
||||
// used for _offset queries with sort, should be removed once the fix is applied to the async path too.
|
||||
private boolean myUseAggregate;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -182,7 +184,7 @@ public class QueryStack {
|
|||
|
||||
addSortCustomJoin(firstPredicateBuilder, datePredicateBuilder, hashIdentityPredicate);
|
||||
|
||||
mySqlBuilder.addSortDate(datePredicateBuilder.getColumnValueLow(), theAscending);
|
||||
mySqlBuilder.addSortDate(datePredicateBuilder.getColumnValueLow(), theAscending, myUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortOnLastUpdated(boolean theAscending) {
|
||||
|
@ -193,7 +195,7 @@ public class QueryStack {
|
|||
} else {
|
||||
resourceTablePredicateBuilder = mySqlBuilder.addResourceTablePredicateBuilder(firstPredicateBuilder.getResourceIdColumn());
|
||||
}
|
||||
mySqlBuilder.addSortDate(resourceTablePredicateBuilder.getColumnLastUpdated(), theAscending);
|
||||
mySqlBuilder.addSortDate(resourceTablePredicateBuilder.getColumnLastUpdated(), theAscending, myUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortOnNumber(String theResourceName, String theParamName, boolean theAscending) {
|
||||
|
@ -204,7 +206,7 @@ public class QueryStack {
|
|||
|
||||
addSortCustomJoin(firstPredicateBuilder, numberPredicateBuilder, hashIdentityPredicate);
|
||||
|
||||
mySqlBuilder.addSortNumeric(numberPredicateBuilder.getColumnValue(), theAscending);
|
||||
mySqlBuilder.addSortNumeric(numberPredicateBuilder.getColumnValue(), theAscending, myUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortOnQuantity(String theResourceName, String theParamName, boolean theAscending) {
|
||||
|
@ -216,18 +218,18 @@ public class QueryStack {
|
|||
|
||||
addSortCustomJoin(firstPredicateBuilder, quantityPredicateBuilder, hashIdentityPredicate);
|
||||
|
||||
mySqlBuilder.addSortNumeric(quantityPredicateBuilder.getColumnValue(), theAscending);
|
||||
mySqlBuilder.addSortNumeric(quantityPredicateBuilder.getColumnValue(), theAscending, myUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortOnResourceId(boolean theAscending) {
|
||||
BaseJoiningPredicateBuilder firstPredicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder();
|
||||
ForcedIdPredicateBuilder sortPredicateBuilder = mySqlBuilder.addForcedIdPredicateBuilder(firstPredicateBuilder.getResourceIdColumn());
|
||||
if (!theAscending) {
|
||||
mySqlBuilder.addSortString(sortPredicateBuilder.getColumnForcedId(), false, OrderObject.NullOrder.FIRST);
|
||||
mySqlBuilder.addSortString(sortPredicateBuilder.getColumnForcedId(), false, OrderObject.NullOrder.FIRST, myUseAggregate);
|
||||
} else {
|
||||
mySqlBuilder.addSortString(sortPredicateBuilder.getColumnForcedId(), true);
|
||||
mySqlBuilder.addSortString(sortPredicateBuilder.getColumnForcedId(), true, myUseAggregate);
|
||||
}
|
||||
mySqlBuilder.addSortNumeric(firstPredicateBuilder.getResourceIdColumn(), theAscending);
|
||||
mySqlBuilder.addSortNumeric(firstPredicateBuilder.getResourceIdColumn(), theAscending, myUseAggregate);
|
||||
|
||||
}
|
||||
|
||||
|
@ -239,7 +241,7 @@ public class QueryStack {
|
|||
|
||||
addSortCustomJoin(firstPredicateBuilder, resourceLinkPredicateBuilder, pathPredicate);
|
||||
|
||||
mySqlBuilder.addSortNumeric(resourceLinkPredicateBuilder.getColumnTargetResourceId(), theAscending);
|
||||
mySqlBuilder.addSortNumeric(resourceLinkPredicateBuilder.getColumnTargetResourceId(), theAscending, myUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortOnString(String theResourceName, String theParamName, boolean theAscending) {
|
||||
|
@ -250,7 +252,7 @@ public class QueryStack {
|
|||
|
||||
addSortCustomJoin(firstPredicateBuilder, stringPredicateBuilder, hashIdentityPredicate);
|
||||
|
||||
mySqlBuilder.addSortString(stringPredicateBuilder.getColumnValueNormalized(), theAscending);
|
||||
mySqlBuilder.addSortString(stringPredicateBuilder.getColumnValueNormalized(), theAscending, myUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortOnToken(String theResourceName, String theParamName, boolean theAscending) {
|
||||
|
@ -261,8 +263,8 @@ public class QueryStack {
|
|||
|
||||
addSortCustomJoin(firstPredicateBuilder, tokenPredicateBuilder, hashIdentityPredicate);
|
||||
|
||||
mySqlBuilder.addSortString(tokenPredicateBuilder.getColumnSystem(), theAscending);
|
||||
mySqlBuilder.addSortString(tokenPredicateBuilder.getColumnValue(), theAscending);
|
||||
mySqlBuilder.addSortString(tokenPredicateBuilder.getColumnSystem(), theAscending, myUseAggregate);
|
||||
mySqlBuilder.addSortString(tokenPredicateBuilder.getColumnValue(), theAscending, myUseAggregate);
|
||||
|
||||
}
|
||||
|
||||
|
@ -274,7 +276,7 @@ public class QueryStack {
|
|||
|
||||
addSortCustomJoin(firstPredicateBuilder, uriPredicateBuilder, hashIdentityPredicate);
|
||||
|
||||
mySqlBuilder.addSortString(uriPredicateBuilder.getColumnValue(), theAscending);
|
||||
mySqlBuilder.addSortString(uriPredicateBuilder.getColumnValue(), theAscending, myUseAggregate);
|
||||
}
|
||||
|
||||
private void addSortCustomJoin(BaseJoiningPredicateBuilder theFromJoiningPredicateBuilder, BaseJoiningPredicateBuilder theToJoiningPredicateBuilder, Condition theCondition){
|
||||
|
@ -292,6 +294,10 @@ public class QueryStack {
|
|||
onCondition);
|
||||
}
|
||||
|
||||
public void setUseAggregate(boolean theUseAggregate) {
|
||||
myUseAggregate = theUseAggregate;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends BaseJoiningPredicateBuilder> PredicateBuilderCacheLookupResult<T> createOrReusePredicateBuilder(PredicateBuilderTypeEnum theType, DbColumn theSourceJoinColumn, String theParamName, Supplier<T> theFactoryMethod) {
|
||||
boolean cacheHit = false;
|
||||
|
|
|
@ -636,6 +636,14 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If offset is present, we want deduplicate the results by using GROUP BY
|
||||
*/
|
||||
if (theOffset != null) {
|
||||
queryStack3.addGrouping();
|
||||
queryStack3.setUseAggregate(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort
|
||||
*
|
||||
|
@ -648,9 +656,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
createSort(queryStack3, sort);
|
||||
}
|
||||
|
||||
if (theOffset != null) {
|
||||
queryStack3.addGrouping();
|
||||
}
|
||||
|
||||
/*
|
||||
* Now perform the search
|
||||
|
|
|
@ -79,7 +79,11 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.*;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS;
|
||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL;
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
|
||||
public class SearchQueryBuilder {
|
||||
|
@ -125,10 +129,10 @@ public class SearchQueryBuilder {
|
|||
mySqlBuilderFactory = theSqlBuilderFactory;
|
||||
myCountQuery = theCountQuery;
|
||||
myDialect = theDialect;
|
||||
if (myDialect instanceof org.hibernate.dialect.MySQLDialect){
|
||||
if (myDialect instanceof org.hibernate.dialect.MySQLDialect) {
|
||||
dialectIsMySql = true;
|
||||
}
|
||||
if (myDialect instanceof org.hibernate.dialect.SQLServerDialect){
|
||||
if (myDialect instanceof org.hibernate.dialect.SQLServerDialect) {
|
||||
dialectIsMsSql = true;
|
||||
}
|
||||
|
||||
|
@ -322,7 +326,7 @@ public class SearchQueryBuilder {
|
|||
/**
|
||||
* Create a predicate builder for selecting on a TOKEN search parameter
|
||||
*/
|
||||
public TokenPredicateBuilder createTokenPredicateBuilder(){
|
||||
public TokenPredicateBuilder createTokenPredicateBuilder() {
|
||||
return mySqlBuilderFactory.tokenIndexTable(this);
|
||||
}
|
||||
|
||||
|
@ -330,7 +334,7 @@ public class SearchQueryBuilder {
|
|||
mySelect.addCustomJoin(theJoinType, theFromTable, theToTable, theCondition);
|
||||
}
|
||||
|
||||
public ComboCondition createOnCondition(DbColumn theSourceColumn, DbColumn theTargetColumn){
|
||||
public ComboCondition createOnCondition(DbColumn theSourceColumn, DbColumn theTargetColumn) {
|
||||
ComboCondition onCondition = ComboCondition.and();
|
||||
onCondition.addCondition(BinaryCondition.equalTo(theSourceColumn, theTargetColumn));
|
||||
|
||||
|
@ -709,21 +713,33 @@ public class SearchQueryBuilder {
|
|||
}
|
||||
|
||||
public void addSortString(DbColumn theColumnValueNormalized, boolean theAscending) {
|
||||
addSortString(theColumnValueNormalized, theAscending, false);
|
||||
}
|
||||
|
||||
public void addSortString(DbColumn theColumnValueNormalized, boolean theAscending, boolean theUseAggregate) {
|
||||
OrderObject.NullOrder nullOrder = OrderObject.NullOrder.LAST;
|
||||
addSortString(theColumnValueNormalized, theAscending, nullOrder);
|
||||
addSortString(theColumnValueNormalized, theAscending, nullOrder, theUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortNumeric(DbColumn theColumnValueNormalized, boolean theAscending) {
|
||||
addSortNumeric(theColumnValueNormalized, theAscending, false);
|
||||
}
|
||||
|
||||
public void addSortNumeric(DbColumn theColumnValueNormalized, boolean theAscending, boolean theUseAggregate) {
|
||||
OrderObject.NullOrder nullOrder = OrderObject.NullOrder.LAST;
|
||||
addSortNumeric(theColumnValueNormalized, theAscending, nullOrder);
|
||||
addSortNumeric(theColumnValueNormalized, theAscending, nullOrder, theUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortDate(DbColumn theColumnValueNormalized, boolean theAscending) {
|
||||
OrderObject.NullOrder nullOrder = OrderObject.NullOrder.LAST;
|
||||
addSortDate(theColumnValueNormalized, theAscending, nullOrder);
|
||||
addSortDate(theColumnValueNormalized, theAscending, false);
|
||||
}
|
||||
|
||||
public void addSortString(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder) {
|
||||
public void addSortDate(DbColumn theColumnValueNormalized, boolean theAscending, boolean theUseAggregate) {
|
||||
OrderObject.NullOrder nullOrder = OrderObject.NullOrder.LAST;
|
||||
addSortDate(theColumnValueNormalized, theAscending, nullOrder, theUseAggregate);
|
||||
}
|
||||
|
||||
public void addSortString(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder, boolean theUseAggregate) {
|
||||
if ((dialectIsMySql || dialectIsMsSql)) {
|
||||
// MariaDB, MySQL and MSSQL do not support "NULLS FIRST" and "NULLS LAST" syntax.
|
||||
String direction = theTheAscending ? " ASC" : " DESC";
|
||||
|
@ -740,14 +756,28 @@ public class SearchQueryBuilder {
|
|||
sortColumnNameBuilder.append( "CASE WHEN " ).append( sortColumnName ).append( " IS NULL THEN 1 ELSE 0 END" ).append(direction).append(", ");
|
||||
}
|
||||
*/
|
||||
sortColumnName = formatColumnNameForAggregate(theTheAscending, theUseAggregate, sortColumnName);
|
||||
sortColumnNameBuilder.append(sortColumnName).append(direction);
|
||||
mySelect.addCustomOrderings(sortColumnNameBuilder.toString());
|
||||
} else {
|
||||
addSort(theTheColumnValueNormalized, theTheAscending, theNullOrder);
|
||||
addSort(theTheColumnValueNormalized, theTheAscending, theNullOrder, theUseAggregate);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSortNumeric(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder) {
|
||||
private static String formatColumnNameForAggregate(boolean theTheAscending, boolean theUseAggregate, String sortColumnName) {
|
||||
if (theUseAggregate) {
|
||||
String aggregateFunction;
|
||||
if (theTheAscending) {
|
||||
aggregateFunction = "MIN";
|
||||
} else {
|
||||
aggregateFunction = "MAX";
|
||||
}
|
||||
sortColumnName = aggregateFunction + "(" + sortColumnName + ")";
|
||||
}
|
||||
return sortColumnName;
|
||||
}
|
||||
|
||||
public void addSortNumeric(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder, boolean theUseAggregate) {
|
||||
if ((dialectIsMySql || dialectIsMsSql)) {
|
||||
// MariaDB, MySQL and MSSQL do not support "NULLS FIRST" and "NULLS LAST" syntax.
|
||||
// Null values are always treated as less than non-null values.
|
||||
|
@ -763,13 +793,14 @@ public class SearchQueryBuilder {
|
|||
} else {
|
||||
direction = theTheAscending ? " ASC" : " DESC";
|
||||
}
|
||||
sortColumnName = formatColumnNameForAggregate(theTheAscending, theUseAggregate, sortColumnName);
|
||||
mySelect.addCustomOrderings(sortColumnName + direction);
|
||||
} else {
|
||||
addSort(theTheColumnValueNormalized, theTheAscending, theNullOrder);
|
||||
addSort(theTheColumnValueNormalized, theTheAscending, theNullOrder, theUseAggregate);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSortDate(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder) {
|
||||
public void addSortDate(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder, boolean theUseAggregate) {
|
||||
if ((dialectIsMySql || dialectIsMsSql)) {
|
||||
// MariaDB, MySQL and MSSQL do not support "NULLS FIRST" and "NULLS LAST" syntax.
|
||||
String direction = theTheAscending ? " ASC" : " DESC";
|
||||
|
@ -786,16 +817,25 @@ public class SearchQueryBuilder {
|
|||
sortColumnNameBuilder.append( "CASE WHEN " ).append( sortColumnName ).append( " IS NULL THEN 1 ELSE 0 END" ).append(direction).append(", ");
|
||||
}
|
||||
*/
|
||||
sortColumnName = formatColumnNameForAggregate(theTheAscending, theUseAggregate, sortColumnName);
|
||||
sortColumnNameBuilder.append(sortColumnName).append(direction);
|
||||
mySelect.addCustomOrderings(sortColumnNameBuilder.toString());
|
||||
} else {
|
||||
addSort(theTheColumnValueNormalized, theTheAscending, theNullOrder);
|
||||
addSort(theTheColumnValueNormalized, theTheAscending, theNullOrder, theUseAggregate);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSort(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder) {
|
||||
private void addSort(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder, boolean theUseAggregate) {
|
||||
OrderObject.Dir direction = theTheAscending ? OrderObject.Dir.ASCENDING : OrderObject.Dir.DESCENDING;
|
||||
OrderObject orderObject = new OrderObject(direction, theTheColumnValueNormalized);
|
||||
Object columnToOrder = theTheColumnValueNormalized;
|
||||
if (theUseAggregate) {
|
||||
if (theTheAscending) {
|
||||
columnToOrder = FunctionCall.min().addColumnParams(theTheColumnValueNormalized);
|
||||
} else {
|
||||
columnToOrder = FunctionCall.max().addColumnParams(theTheColumnValueNormalized);
|
||||
}
|
||||
}
|
||||
OrderObject orderObject = new OrderObject(direction, columnToOrder);
|
||||
orderObject.setNullOrder(theNullOrder);
|
||||
mySelect.addCustomOrderings(orderObject);
|
||||
}
|
||||
|
@ -804,7 +844,7 @@ public class SearchQueryBuilder {
|
|||
* If set to true (default is false), force the generated SQL to start
|
||||
* with the {@link ca.uhn.fhir.jpa.model.entity.ResourceTable HFJ_RESOURCE}
|
||||
* table at the root of the query.
|
||||
*
|
||||
* <p>
|
||||
* This seems to perform better if there are multiple joins on the
|
||||
* resource ID table.
|
||||
*/
|
||||
|
|
|
@ -57,7 +57,7 @@ public abstract class BaseSearchQueryBuilderDialectTest {
|
|||
if (theNullOrder == null) {
|
||||
searchQueryBuilder.addSortNumeric(sortPredicateBuilder.getColumnValueLow(), theAscending);
|
||||
} else {
|
||||
searchQueryBuilder.addSortNumeric(sortPredicateBuilder.getColumnValueLow(), theAscending, theNullOrder);
|
||||
searchQueryBuilder.addSortNumeric(sortPredicateBuilder.getColumnValueLow(), theAscending, theNullOrder, false);
|
||||
}
|
||||
|
||||
return searchQueryBuilder.generate(0, 500);
|
||||
|
|
|
@ -70,7 +70,7 @@ public class SearchQueryBuilderDialectMySqlTest extends BaseSearchQueryBuilderDi
|
|||
if (theNullOrder == null) {
|
||||
searchQueryBuilder.addSortString(sortPredicateBuilder.getColumnValueNormalized(), theAscending);
|
||||
} else {
|
||||
searchQueryBuilder.addSortString(sortPredicateBuilder.getColumnValueNormalized(), theAscending, theNullOrder);
|
||||
searchQueryBuilder.addSortString(sortPredicateBuilder.getColumnValueNormalized(), theAscending, theNullOrder, false);
|
||||
}
|
||||
|
||||
return searchQueryBuilder.generate(0,500);
|
||||
|
@ -119,7 +119,7 @@ public class SearchQueryBuilderDialectMySqlTest extends BaseSearchQueryBuilderDi
|
|||
if (theNullOrder == null) {
|
||||
searchQueryBuilder.addSortDate(sortPredicateBuilder.getColumnValueLow(), theAscending);
|
||||
} else {
|
||||
searchQueryBuilder.addSortDate(sortPredicateBuilder.getColumnValueLow(), theAscending, theNullOrder);
|
||||
searchQueryBuilder.addSortDate(sortPredicateBuilder.getColumnValueLow(), theAscending, theNullOrder, false);
|
||||
}
|
||||
|
||||
return searchQueryBuilder.generate(0,500);
|
||||
|
|
Loading…
Reference in New Issue