From 1407f3c63cc3a871e3b1563975fd70521dcdbd62 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 24 Apr 2023 12:12:41 -0500 Subject: [PATCH] Add count distinct option to count action --- .../actions/tables/count/CountInput.java | 34 +++++++++++++- .../actions/tables/count/CountOutput.java | 44 +++++++++++++++++++ .../rdbms/actions/RDBMSCountAction.java | 11 +++++ .../javalin/QJavalinImplementation.java | 6 +++ .../qqq/backend/javalin/QJavalinUtils.java | 18 ++++++++ 5 files changed, 112 insertions(+), 1 deletion(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountInput.java index 09ff116c..1a4863e5 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountInput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountInput.java @@ -37,7 +37,8 @@ public class CountInput extends AbstractTableActionInput { private QQueryFilter filter; - private List queryJoins = null; + private List queryJoins = null; + private Boolean includeDistinctCount = false; @@ -120,4 +121,35 @@ public class CountInput extends AbstractTableActionInput return (this); } + + + /******************************************************************************* + ** Getter for includeDistinctCount + *******************************************************************************/ + public Boolean getIncludeDistinctCount() + { + return (this.includeDistinctCount); + } + + + + /******************************************************************************* + ** Setter for includeDistinctCount + *******************************************************************************/ + public void setIncludeDistinctCount(Boolean includeDistinctCount) + { + this.includeDistinctCount = includeDistinctCount; + } + + + + /******************************************************************************* + ** Fluent setter for includeDistinctCount + *******************************************************************************/ + public CountInput withIncludeDistinctCount(Boolean includeDistinctCount) + { + this.includeDistinctCount = includeDistinctCount; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountOutput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountOutput.java index d3703cfa..0484a175 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountOutput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/count/CountOutput.java @@ -32,6 +32,7 @@ import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput; public class CountOutput extends AbstractActionOutput { private Integer count; + private Integer distinctCount; @@ -52,4 +53,47 @@ public class CountOutput extends AbstractActionOutput { this.count = count; } + + + + /******************************************************************************* + ** Getter for distinctCount + *******************************************************************************/ + public Integer getDistinctCount() + { + return (this.distinctCount); + } + + + + /******************************************************************************* + ** Setter for distinctCount + *******************************************************************************/ + public void setDistinctCount(Integer distinctCount) + { + this.distinctCount = distinctCount; + } + + + + /******************************************************************************* + ** Fluent setter for distinctCount + *******************************************************************************/ + public CountOutput withDistinctCount(Integer distinctCount) + { + this.distinctCount = distinctCount; + return (this); + } + + + + /******************************************************************************* + ** Fluent setter for count + *******************************************************************************/ + public CountOutput withCount(Integer count) + { + this.count = count; + return (this); + } + } diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java index dad1067f..e713c1b5 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java @@ -36,6 +36,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; +import org.apache.commons.lang.BooleanUtils; /******************************************************************************* @@ -63,6 +64,11 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf String primaryKeyColumn = escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(fieldAndTableNameOrAlias.field().getName()); String clausePrefix = (requiresDistinct) ? "SELECT COUNT(DISTINCT (" + primaryKeyColumn + "))" : "SELECT COUNT(*)"; + if(BooleanUtils.isTrue(countInput.getIncludeDistinctCount())) + { + clausePrefix = "SELECT COUNT(DISTINCT (" + primaryKeyColumn + ")) AS distinct_count, COUNT(*)"; + } + String sql = clausePrefix + " AS record_count FROM " + makeFromClause(countInput.getInstance(), table.getName(), joinsContext); @@ -81,6 +87,11 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf if(resultSet.next()) { rs.setCount(resultSet.getInt("record_count")); + + if(BooleanUtils.isTrue(countInput.getIncludeDistinctCount())) + { + rs.setDistinctCount(resultSet.getInt("distinct_count")); + } } }), params); diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java index 62c88550..a8eaae97 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java @@ -788,6 +788,7 @@ public class QJavalinImplementation } countInput.setQueryJoins(processQueryJoinsParam(context)); + countInput.setIncludeDistinctCount(QJavalinUtils.queryParamIsTrue(context, "includeDistinct")); CountAction countAction = new CountAction(); CountOutput countOutput = countAction.execute(countInput); @@ -861,6 +862,11 @@ public class QJavalinImplementation QueryAction queryAction = new QueryAction(); QueryOutput queryOutput = queryAction.execute(queryInput); + int rowIndex = 0; + for(QRecord record : queryOutput.getRecords()) + { + record.setValue("__qRowIndex", rowIndex++); + } QJavalinAccessLogger.logEndSuccess(logPair("recordCount", queryOutput.getRecords().size()), logPairIfSlow("filter", filter, SLOW_LOG_THRESHOLD_MS)); context.result(JsonUtils.toJson(queryOutput)); diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinUtils.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinUtils.java index d8fa1ebe..fb1a4681 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinUtils.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinUtils.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.backend.javalin; +import java.util.Objects; import com.kingsrook.qqq.backend.core.exceptions.QValueException; import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils; @@ -52,6 +53,23 @@ public class QJavalinUtils + /******************************************************************************* + ** Returns true iff context has a valid query parameter by the given name, with + ** a value of "true". + *******************************************************************************/ + public static boolean queryParamIsTrue(Context context, String name) throws QValueException + { + String value = context.queryParam(name); + if(Objects.equals(value, "true")) + { + return (true); + } + + return (false); + } + + + /******************************************************************************* ** Returns Integer if context has a valid int form parameter by the given name, ** Returns null if no param (or empty value).