SPRINT-17: updates to parent widget dropdown data, updated group bys to be objects allowing group by with custom formats

This commit is contained in:
Tim Chamberlain
2022-12-07 15:31:48 -06:00
parent 241741e2e5
commit 9b34ee7fe7
14 changed files with 766 additions and 67 deletions

View File

@ -31,6 +31,9 @@ import java.util.Set;
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceInput;
import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceOutput;
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
@ -40,6 +43,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.ParentWidgetMetaD
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
@ -68,15 +72,42 @@ public class ParentWidgetRenderer extends AbstractWidgetRenderer
List<List<Map<String, String>>> pvsData = new ArrayList<>();
List<String> pvsLabels = new ArrayList<>();
List<String> pvsNames = new ArrayList<>();
for(String possibleValueSourceName : CollectionUtils.nonNullList(metaData.getPossibleValueNameList()))
for(ParentWidgetMetaData.DropdownData dropdownData : CollectionUtils.nonNullList(metaData.getDropdowns()))
{
QPossibleValueSource possibleValueSource = input.getInstance().getPossibleValueSource(possibleValueSourceName);
pvsLabels.add(possibleValueSource.getLabel() != null ? possibleValueSource.getLabel() : possibleValueSourceName);
String possibleValueSourceName = dropdownData.getPossibleValueSourceName();
QPossibleValueSource possibleValueSource = input.getInstance().getPossibleValueSource(possibleValueSourceName);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// this looks complicated, but is just look for a label in the dropdown data and if found use it, //
// otherwise look for label in PVS and if found use that, otherwise just use the PVS name //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
pvsLabels.add(dropdownData.getLabel() != null ? dropdownData.getLabel() : (possibleValueSource.getLabel() != null ? possibleValueSource.getLabel() : possibleValueSourceName));
pvsNames.add(possibleValueSourceName);
SearchPossibleValueSourceInput pvsInput = new SearchPossibleValueSourceInput(input.getInstance());
pvsInput.setSession(input.getSession());
pvsInput.setPossibleValueSourceName(possibleValueSourceName);
if(dropdownData.getForeignKeyFieldName() != null)
{
////////////////////////////////////////
// look for an id in the query params //
////////////////////////////////////////
Integer id = null;
if(input.getQueryParams() != null && input.getQueryParams().containsKey("id") && StringUtils.hasContent(input.getQueryParams().get("id")))
{
id = Integer.parseInt(input.getQueryParams().get("id"));
}
if(id != null)
{
pvsInput.setDefaultQueryFilter(new QQueryFilter().withCriteria(
new QFilterCriteria(
dropdownData.getForeignKeyFieldName(),
QCriteriaOperator.EQUALS,
id)));
}
}
SearchPossibleValueSourceOutput output = new SearchPossibleValueSourceAction().execute(pvsInput);
List<Map<String, String>> dropdownOptionList = new ArrayList<>();

View File

@ -38,7 +38,7 @@ public class AggregateInput extends AbstractTableActionInput
{
private QQueryFilter filter;
private List<Aggregate> aggregates;
private List<String> groupByFieldNames;
private List<GroupBy> groupBys = new ArrayList<>();
private List<QueryJoin> queryJoins = null;
@ -148,50 +148,50 @@ public class AggregateInput extends AbstractTableActionInput
/*******************************************************************************
** Getter for groupByFieldNames
** Getter for groupBys
**
*******************************************************************************/
public List<String> getGroupByFieldNames()
public List<GroupBy> getGroupBys()
{
return groupByFieldNames;
return groupBys;
}
/*******************************************************************************
** Setter for groupByFieldNames
** Setter for groupBys
**
*******************************************************************************/
public void setGroupByFieldNames(List<String> groupByFieldNames)
public void setGroupBys(List<GroupBy> groupBys)
{
this.groupByFieldNames = groupByFieldNames;
this.groupBys = groupBys;
}
/*******************************************************************************
** Fluent setter for groupByFieldNames
** Fluent setter for groupBys
**
*******************************************************************************/
public AggregateInput withGroupByFieldNames(List<String> groupByFieldNames)
public AggregateInput withGroupBys(List<GroupBy> groupBys)
{
this.groupByFieldNames = groupByFieldNames;
this.groupBys = groupBys;
return (this);
}
/*******************************************************************************
** Fluent setter for groupByFieldNames
** Fluent setter for groupBys
**
*******************************************************************************/
public AggregateInput withGroupByFieldName(String groupByFieldName)
public AggregateInput withGroupBy(GroupBy groupBy)
{
if(this.groupByFieldNames == null)
if(this.groupBys == null)
{
this.groupByFieldNames = new ArrayList<>();
this.groupBys = new ArrayList<>();
}
this.groupByFieldNames.add(groupByFieldName);
this.groupBys.add(groupBy);
return (this);
}

View File

@ -33,7 +33,7 @@ import java.util.Map;
public class AggregateResult
{
private Map<Aggregate, Serializable> aggregateValues = new LinkedHashMap<>();
private Map<String, Serializable> groupByValues = new LinkedHashMap<>();
private Map<GroupBy, Serializable> groupByValues = new LinkedHashMap<>();
@ -101,7 +101,7 @@ public class AggregateResult
** Getter for groupByValues
**
*******************************************************************************/
public Map<String, Serializable> getGroupByValues()
public Map<GroupBy, Serializable> getGroupByValues()
{
return groupByValues;
}
@ -112,7 +112,7 @@ public class AggregateResult
** Setter for groupByValues
**
*******************************************************************************/
public void setGroupByValues(Map<String, Serializable> groupByValues)
public void setGroupByValues(Map<GroupBy, Serializable> groupByValues)
{
this.groupByValues = groupByValues;
}
@ -123,7 +123,7 @@ public class AggregateResult
** Fluent setter for groupByValues
**
*******************************************************************************/
public AggregateResult withGroupByValues(Map<String, Serializable> groupByValues)
public AggregateResult withGroupByValues(Map<GroupBy, Serializable> groupByValues)
{
this.groupByValues = groupByValues;
return (this);
@ -135,13 +135,13 @@ public class AggregateResult
** Fluent setter for groupByValues
**
*******************************************************************************/
public AggregateResult withGroupByValue(String fieldName, Serializable value)
public AggregateResult withGroupByValue(GroupBy groupBy, Serializable value)
{
if(this.groupByValues == null)
{
this.groupByValues = new LinkedHashMap<>();
}
this.groupByValues.put(fieldName, value);
this.groupByValues.put(groupBy, value);
return (this);
}
@ -150,9 +150,9 @@ public class AggregateResult
/*******************************************************************************
**
*******************************************************************************/
public Serializable getGroupByValue(String fieldName)
public Serializable getGroupByValue(GroupBy groupBy)
{
return (this.groupByValues.get(fieldName));
return (this.groupByValues.get(groupBy));
}
}

View File

@ -0,0 +1,152 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.actions.tables.aggregate;
import java.io.Serializable;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
/*******************************************************************************
**
*******************************************************************************/
public class GroupBy implements Serializable
{
private QFieldType type;
private String fieldName;
private String formatString;
/*******************************************************************************
**
*******************************************************************************/
public GroupBy(QFieldType type, String fieldName, String formatString)
{
this.type = type;
this.fieldName = fieldName;
this.formatString = formatString;
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public QFieldType getType()
{
return type;
}
/*******************************************************************************
** Setter for type
**
*******************************************************************************/
public void setType(QFieldType type)
{
this.type = type;
}
/*******************************************************************************
** Fluent setter for type
**
*******************************************************************************/
public GroupBy withType(QFieldType type)
{
this.type = type;
return (this);
}
/*******************************************************************************
** Getter for fieldName
**
*******************************************************************************/
public String getFieldName()
{
return fieldName;
}
/*******************************************************************************
** Setter for fieldName
**
*******************************************************************************/
public void setFieldName(String fieldName)
{
this.fieldName = fieldName;
}
/*******************************************************************************
** Fluent setter for fieldName
**
*******************************************************************************/
public GroupBy withFieldName(String fieldName)
{
this.fieldName = fieldName;
return (this);
}
/*******************************************************************************
** Getter for formatString
**
*******************************************************************************/
public String getFormatString()
{
return formatString;
}
/*******************************************************************************
** Setter for formatString
**
*******************************************************************************/
public void setFormatString(String formatString)
{
this.formatString = formatString;
}
/*******************************************************************************
** Fluent setter for formatString
**
*******************************************************************************/
public GroupBy withFormatString(String formatString)
{
this.formatString = formatString;
return (this);
}
}

View File

@ -0,0 +1,123 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.actions.tables.aggregate;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
/*******************************************************************************
** Bean representing an element of a query order-by clause - ordering by a
** group by
**
*******************************************************************************/
public class QFilterOrderByGroupBy extends QFilterOrderBy implements Cloneable
{
private GroupBy groupBy;
/*******************************************************************************
**
*******************************************************************************/
@Override
public QFilterOrderByGroupBy clone()
{
return (QFilterOrderByGroupBy) super.clone();
}
/*******************************************************************************
** Default no-arg constructor
*******************************************************************************/
public QFilterOrderByGroupBy()
{
}
/*******************************************************************************
** Constructor that sets groupBy, but leaves default for isAscending (true)
*******************************************************************************/
public QFilterOrderByGroupBy(GroupBy groupBy)
{
this.groupBy = groupBy;
}
/*******************************************************************************
** Constructor that takes groupBy and isAscending.
*******************************************************************************/
public QFilterOrderByGroupBy(GroupBy groupBy, boolean isAscending)
{
this.groupBy = groupBy;
setIsAscending(isAscending);
}
/*******************************************************************************
** Getter for groupBy
**
*******************************************************************************/
public GroupBy getGroupBy()
{
return groupBy;
}
/*******************************************************************************
** Setter for groupBy
**
*******************************************************************************/
public void setGroupBy(GroupBy groupBy)
{
this.groupBy = groupBy;
}
/*******************************************************************************
** Fluent setter for groupBy
**
*******************************************************************************/
public QFilterOrderByGroupBy withGroupBy(GroupBy groupBy)
{
this.groupBy = groupBy;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String toString()
{
return (groupBy + " " + (getIsAscending() ? "ASC" : "DESC"));
}
}

View File

@ -41,9 +41,21 @@ public class ChartData implements QWidget
}
*/
private String title;
private String description;
private Data chartData;
private String title;
private String description;
private List<String> colors;
private Data chartData;
private boolean isCurrency = false;
private int height;
/*******************************************************************************
**
*******************************************************************************/
public ChartData()
{
}
@ -176,6 +188,74 @@ public class ChartData implements QWidget
/*******************************************************************************
** Getter for isCurrency
**
*******************************************************************************/
public boolean getIsCurrency()
{
return isCurrency;
}
/*******************************************************************************
** Setter for isCurrency
**
*******************************************************************************/
public void setIsCurrency(boolean isCurrency)
{
this.isCurrency = isCurrency;
}
/*******************************************************************************
** Fluent setter for isCurrency
**
*******************************************************************************/
public ChartData withIsCurrency(boolean isCurrency)
{
this.isCurrency = isCurrency;
return (this);
}
/*******************************************************************************
** Getter for height
**
*******************************************************************************/
public int getHeight()
{
return height;
}
/*******************************************************************************
** Setter for height
**
*******************************************************************************/
public void setHeight(int height)
{
this.height = height;
}
/*******************************************************************************
** Fluent setter for height
**
*******************************************************************************/
public ChartData withHeight(int height)
{
this.height = height;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
@ -224,9 +304,13 @@ public class ChartData implements QWidget
** Getter for datasets
**
*******************************************************************************/
public Dataset getDataset()
public List<Dataset> getDatasets()
{
return dataset;
if(dataset != null)
{
return List.of(dataset);
}
return List.of();
}
@ -260,6 +344,7 @@ public class ChartData implements QWidget
public static class Dataset
{
private String label;
private String color;
private List<Number> data;
@ -286,6 +371,40 @@ public class ChartData implements QWidget
/*******************************************************************************
** Getter for color
**
*******************************************************************************/
public String getColor()
{
return color;
}
/*******************************************************************************
** Setter for color
**
*******************************************************************************/
public void setColor(String color)
{
this.color = color;
}
/*******************************************************************************
** Fluent setter for color
**
*******************************************************************************/
public Dataset withColor(String color)
{
this.color = color;
return (this);
}
/*******************************************************************************
** Fluent setter for label
**

View File

@ -44,8 +44,9 @@ public class LineChartData implements QWidget
};
*/
private String title;
private Data chartData;
private String title;
private Data chartData;
private boolean isYAxisCurrency = false;
@ -151,6 +152,40 @@ public class LineChartData implements QWidget
/*******************************************************************************
** Getter for isYAxisCurrency
**
*******************************************************************************/
public boolean getIsYAxisCurrency()
{
return isYAxisCurrency;
}
/*******************************************************************************
** Setter for isYAxisCurrency
**
*******************************************************************************/
public void setIsYAxisCurrency(boolean isYAxisCurrency)
{
this.isYAxisCurrency = isYAxisCurrency;
}
/*******************************************************************************
** Fluent setter for isYAxisCurrency
**
*******************************************************************************/
public LineChartData withIsYAxisCurrency(boolean isYAxisCurrency)
{
this.isYAxisCurrency = isYAxisCurrency;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -38,17 +38,18 @@ public class StatisticsData implements QWidget
}
*/
private String title;
private int count;
private Number percentageAmount;
private String percentageLabel;
private String title;
private Number count;
private Number percentageAmount;
private String percentageLabel;
private boolean isCurrency = false;
/*******************************************************************************
**
*******************************************************************************/
public StatisticsData(String title, int count, Number percentageAmount, String percentageLabel)
public StatisticsData(String title, Number count, Number percentageAmount, String percentageLabel)
{
this.title = title;
this.count = count;
@ -107,7 +108,7 @@ public class StatisticsData implements QWidget
** Getter for count
**
*******************************************************************************/
public int getCount()
public Number getCount()
{
return count;
}
@ -118,7 +119,7 @@ public class StatisticsData implements QWidget
** Setter for count
**
*******************************************************************************/
public void setCount(int count)
public void setCount(Number count)
{
this.count = count;
}
@ -129,7 +130,7 @@ public class StatisticsData implements QWidget
** Fluent setter for count
**
*******************************************************************************/
public StatisticsData withCount(int count)
public StatisticsData withCount(Number count)
{
this.count = count;
return (this);
@ -203,4 +204,38 @@ public class StatisticsData implements QWidget
return (this);
}
/*******************************************************************************
** Getter for isCurrency
**
*******************************************************************************/
public boolean getIsCurrency()
{
return isCurrency;
}
/*******************************************************************************
** Setter for isCurrency
**
*******************************************************************************/
public void setIsCurrency(boolean isCurrency)
{
this.isCurrency = isCurrency;
}
/*******************************************************************************
** Fluent setter for isCurrency
**
*******************************************************************************/
public StatisticsData withIsCurrency(boolean isCurrency)
{
this.isCurrency = isCurrency;
return (this);
}
}

View File

@ -27,9 +27,11 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
*******************************************************************************/
public enum WidgetType
{
BAR_CHART("barChart"),
CHART("chart"),
CHILD_RECORD_LIST("childRecordList"),
GENERIC("generic"),
HORIZONTAL_BAR_CHART("horizontalBarChart"),
HTML("html"),
LINE_CHART("lineChart"),
LOCATION("location"),

View File

@ -31,10 +31,11 @@ import java.util.List;
*******************************************************************************/
public class ParentWidgetMetaData extends QWidgetMetaData implements QWidgetMetaDataInterface
{
private String title;
private List<String> possibleValueNameList;
private List<String> childWidgetNameList;
private List<String> childProcessNameList;
private String title;
private List<String> possibleValueNameList;
private List<String> childWidgetNameList;
private List<String> childProcessNameList;
private List<DropdownData> dropdowns;
@ -172,4 +173,154 @@ public class ParentWidgetMetaData extends QWidgetMetaData implements QWidgetMeta
return (this);
}
/*******************************************************************************
** Getter for dropdowns
**
*******************************************************************************/
public List<DropdownData> getDropdowns()
{
return dropdowns;
}
/*******************************************************************************
** Setter for dropdowns
**
*******************************************************************************/
public void setDropdowns(List<DropdownData> dropdowns)
{
this.dropdowns = dropdowns;
}
/*******************************************************************************
** Fluent setter for dropdowns
**
*******************************************************************************/
public ParentWidgetMetaData withDropdowns(List<DropdownData> dropdowns)
{
this.dropdowns = dropdowns;
return (this);
}
/*******************************************************************************
** inner class for specifying details about dropdown fields on a parent widget
**
*******************************************************************************/
public static class DropdownData
{
private String possibleValueSourceName;
private String foreignKeyFieldName;
private String label;
/*******************************************************************************
** Getter for possibleValueSourceName
**
*******************************************************************************/
public String getPossibleValueSourceName()
{
return possibleValueSourceName;
}
/*******************************************************************************
** Setter for possibleValueSourceName
**
*******************************************************************************/
public void setPossibleValueSourceName(String possibleValueSourceName)
{
this.possibleValueSourceName = possibleValueSourceName;
}
/*******************************************************************************
** Fluent setter for possibleValueSourceName
**
*******************************************************************************/
public DropdownData withPossibleValueSourceName(String possibleValueSourceName)
{
this.possibleValueSourceName = possibleValueSourceName;
return (this);
}
/*******************************************************************************
** Getter for foreignKeyFieldName
**
*******************************************************************************/
public String getForeignKeyFieldName()
{
return foreignKeyFieldName;
}
/*******************************************************************************
** Setter for foreignKeyFieldName
**
*******************************************************************************/
public void setForeignKeyFieldName(String foreignKeyFieldName)
{
this.foreignKeyFieldName = foreignKeyFieldName;
}
/*******************************************************************************
** Fluent setter for foreignKeyFieldName
**
*******************************************************************************/
public DropdownData withForeignKeyFieldName(String foreignKeyFieldName)
{
this.foreignKeyFieldName = foreignKeyFieldName;
return (this);
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
** Fluent setter for label
**
*******************************************************************************/
public DropdownData withLabel(String label)
{
this.label = label;
return (this);
}
}
}

View File

@ -197,6 +197,23 @@ public class GeneralProcessUtils
/*******************************************************************************
** Query to get one entity by a unique key value. That field can be the primary
** key, or any other field on the table. Note, if multiple rows do match the value,
** only 1 (determined in an unspecified way) is returned.
*******************************************************************************/
public static <T extends QRecordEntity> Optional<T> getEntityByField(AbstractActionInput parentActionInput, String tableName, String fieldName, Serializable fieldValue, Class<T> entityClass) throws QException
{
Optional<QRecord> optionalQRecord = getRecordByField(parentActionInput, tableName, fieldName, fieldValue);
if(optionalQRecord.isPresent())
{
return (Optional.of(QRecordEntity.fromQRecord(entityClass, optionalQRecord.get())));
}
return (Optional.empty());
}
/*******************************************************************************
** Query to get one record by a unique key value.
*******************************************************************************/

View File

@ -37,7 +37,9 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.QActionInterface;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
@ -123,7 +125,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface
*******************************************************************************/
protected Serializable scrubValue(QFieldMetaData field, Serializable value, boolean isInsert)
{
if("".equals(value))
if("" .equals(value))
{
QFieldType type = field.getType();
if(type.equals(QFieldType.INTEGER) || type.equals(QFieldType.DECIMAL) || type.equals(QFieldType.DATE) || type.equals(QFieldType.DATE_TIME) || type.equals(QFieldType.BOOLEAN))
@ -513,9 +515,9 @@ public abstract class AbstractRDBMSAction implements QActionInterface
/*******************************************************************************
**
*******************************************************************************/
protected Serializable getFieldValueFromResultSet(QFieldMetaData qFieldMetaData, ResultSet resultSet, int i) throws SQLException
protected Serializable getFieldValueFromResultSet(QFieldType type, ResultSet resultSet, int i) throws SQLException
{
switch(qFieldMetaData.getType())
switch(type)
{
case STRING:
case TEXT:
@ -551,10 +553,19 @@ public abstract class AbstractRDBMSAction implements QActionInterface
}
default:
{
throw new IllegalStateException("Unexpected field type: " + qFieldMetaData.getType());
throw new IllegalStateException("Unexpected field type: " + type);
}
}
}
/*******************************************************************************
**
*******************************************************************************/
protected Serializable getFieldValueFromResultSet(QFieldMetaData qFieldMetaData, ResultSet resultSet, int i) throws SQLException
{
return (getFieldValueFromResultSet(qFieldMetaData.getType(), resultSet, i));
}
@ -575,6 +586,10 @@ public abstract class AbstractRDBMSAction implements QActionInterface
String clause = (aggregate.getOperator() + "(" + escapeIdentifier(getColumnName(table.getField(aggregate.getFieldName()))) + ")");
clauses.add(clause + " " + ascOrDesc);
}
else if(orderBy instanceof QFilterOrderByGroupBy orderByGroupBy)
{
clauses.add(getSingleGroupByClause(orderByGroupBy.getGroupBy(), joinsContext) + " " + ascOrDesc);
}
else
{
JoinsContext.FieldAndTableNameOrAlias otherFieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(orderBy.getFieldName());
@ -586,4 +601,23 @@ public abstract class AbstractRDBMSAction implements QActionInterface
}
return (String.join(", ", clauses));
}
/*******************************************************************************
**
*******************************************************************************/
protected String getSingleGroupByClause(GroupBy groupBy, JoinsContext joinsContext)
{
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupBy.getFieldName());
String fullFieldName = escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field()));
if(groupBy.getFormatString() == null)
{
return (fullFieldName);
}
else
{
return (String.format(groupBy.getFormatString(), fullFieldName));
}
}
}

View File

@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateIn
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateResult;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
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.fields.QFieldMetaData;
@ -78,7 +79,7 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
sql += " WHERE " + makeWhereClause(aggregateInput.getInstance(), table, joinsContext, filter, params);
}
if(CollectionUtils.nullSafeHasContents(aggregateInput.getGroupByFieldNames()))
if(CollectionUtils.nullSafeHasContents(aggregateInput.getGroupBys()))
{
sql += " GROUP BY " + makeGroupByClause(aggregateInput, joinsContext);
}
@ -105,11 +106,10 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
results.add(result);
int selectionIndex = 1;
for(String groupByFieldName : CollectionUtils.nonNullList(aggregateInput.getGroupByFieldNames()))
for(GroupBy groupBy : CollectionUtils.nonNullList(aggregateInput.getGroupBys()))
{
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName);
Serializable value = getFieldValueFromResultSet(fieldAndTableNameOrAlias.field(), resultSet, selectionIndex++);
result.withGroupByValue(groupByFieldName, value);
Serializable value = getFieldValueFromResultSet(groupBy.getType(), resultSet, selectionIndex++);
result.withGroupByValue(groupBy, value);
}
for(Aggregate aggregate : aggregateInput.getAggregates())
@ -148,10 +148,9 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
{
List<String> rs = new ArrayList<>();
for(String groupByFieldName : CollectionUtils.nonNullList(aggregateInput.getGroupByFieldNames()))
for(GroupBy groupBy : CollectionUtils.nonNullList(aggregateInput.getGroupBys()))
{
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName);
rs.add(escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field())));
rs.add(getSingleGroupByClause(groupBy, joinsContext));
}
for(Aggregate aggregate : aggregateInput.getAggregates())
@ -170,10 +169,9 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
private String makeGroupByClause(AggregateInput aggregateInput, JoinsContext joinsContext)
{
List<String> columns = new ArrayList<>();
for(String groupByFieldName : aggregateInput.getGroupByFieldNames())
for(GroupBy groupBy : CollectionUtils.nonNullList(aggregateInput.getGroupBys()))
{
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName);
columns.add(escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field())));
columns.add(getSingleGroupByClause(groupBy, joinsContext));
}
return (StringUtils.join(",", columns));

View File

@ -32,6 +32,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateIn
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateResult;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
@ -40,6 +41,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Assertions;
@ -145,7 +147,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
aggregateInput.withAggregate(countOfId);
aggregateInput.withAggregate(sumOfDaysWorked);
aggregateInput.withGroupByFieldName("lastName");
aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null));
aggregateInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderBy("lastName")));
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
@ -182,8 +184,8 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
aggregateInput.withAggregate(countOfId);
aggregateInput.withAggregate(sumOfDaysWorked);
aggregateInput.withGroupByFieldName("lastName");
aggregateInput.withGroupByFieldName("firstName");
aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null));
aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "firstName", null));
aggregateInput.setFilter(new QQueryFilter()
.withOrderBy(new QFilterOrderBy("lastName"))
@ -238,7 +240,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
aggregateInput.withAggregate(countOfId);
// note - don't query this value - just order by it!! aggregateInput.withAggregate(sumOfDaysWorked);
aggregateInput.withGroupByFieldName("lastName");
aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null));
aggregateInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderByAggregate(sumOfDaysWorked, false)));
@ -290,7 +292,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
/////////////////////////////////////////////////////////////////////////////////////////
// but re-run w/ a group-by -- then, if no rows are found, there are 0 result objects. //
/////////////////////////////////////////////////////////////////////////////////////////
aggregateInput.withGroupByFieldName("lastName");
aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null));
aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);
assertTrue(aggregateOutput.getResults().isEmpty());
}
@ -328,7 +330,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest
aggregateInput.setSession(new QSession());
aggregateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
aggregateInput.withAggregate(sumOfQuantity);
aggregateInput.withGroupByFieldName(TestUtils.TABLE_NAME_ORDER_LINE + ".sku");
aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, TestUtils.TABLE_NAME_ORDER_LINE + ".sku", null));
aggregateInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_ORDER_LINE));
AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput);