mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Much implementation of joins for RDBMS
This commit is contained in:
@ -46,6 +46,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutp
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
@ -185,7 +186,7 @@ public class GenerateReportAction
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void startTableView(ReportInput reportInput, QReportDataSource dataSource, QReportView reportView) throws QReportingException
|
||||
private void startTableView(ReportInput reportInput, QReportDataSource dataSource, QReportView reportView) throws QException
|
||||
{
|
||||
QTableMetaData table = reportInput.getInstance().getTable(dataSource.getSourceTable());
|
||||
|
||||
@ -200,6 +201,8 @@ public class GenerateReportAction
|
||||
exportInput.setIncludeHeaderRow(reportView.getIncludeHeaderRow());
|
||||
exportInput.setReportOutputStream(reportInput.getReportOutputStream());
|
||||
|
||||
JoinsContext joinsContext = new JoinsContext(exportInput.getInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins());
|
||||
|
||||
List<QFieldMetaData> fields;
|
||||
if(CollectionUtils.nullSafeHasContents(reportView.getColumns()))
|
||||
{
|
||||
@ -212,7 +215,14 @@ public class GenerateReportAction
|
||||
}
|
||||
else
|
||||
{
|
||||
QFieldMetaData field = table.getField(column.getName()).clone();
|
||||
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(column.getName());
|
||||
if(fieldAndTableNameOrAlias.field() == null)
|
||||
{
|
||||
throw new QReportingException("Could not find field named [" + column.getName() + "] on table [" + table.getName() + "]");
|
||||
}
|
||||
|
||||
QFieldMetaData field = fieldAndTableNameOrAlias.field().clone();
|
||||
field.setName(column.getName());
|
||||
if(StringUtils.hasContent(column.getLabel()))
|
||||
{
|
||||
field.setLabel(column.getLabel());
|
||||
@ -278,6 +288,7 @@ public class GenerateReportAction
|
||||
queryInput.setRecordPipe(recordPipe);
|
||||
queryInput.setTableName(dataSource.getSourceTable());
|
||||
queryInput.setFilter(queryFilter);
|
||||
queryInput.setQueryJoins(dataSource.getQueryJoins());
|
||||
queryInput.setShouldTranslatePossibleValues(true); // todo - any limits or conditions on this?
|
||||
return (new QueryAction().execute(queryInput));
|
||||
}
|
||||
|
@ -49,7 +49,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponen
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
@ -365,6 +367,50 @@ public class QInstanceEnricher
|
||||
{
|
||||
report.getInputFields().forEach(this::enrichField);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if there's only 1 data source in the report, and it doesn't have a name, give it a default name //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
String singleDataSourceName = null;
|
||||
if(report.getDataSources() != null)
|
||||
{
|
||||
if(report.getDataSources().size() == 1)
|
||||
{
|
||||
QReportDataSource dataSource = report.getDataSources().get(0);
|
||||
if(!StringUtils.hasContent(dataSource.getName()))
|
||||
{
|
||||
dataSource.setName("DEFAULT");
|
||||
}
|
||||
singleDataSourceName = dataSource.getName();
|
||||
}
|
||||
}
|
||||
|
||||
if(report.getViews() != null)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if there's only 1 view in the report, and it doesn't have a name, give it a default name //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(report.getViews().size() == 1)
|
||||
{
|
||||
QReportView view = report.getViews().get(0);
|
||||
if(!StringUtils.hasContent(view.getName()))
|
||||
{
|
||||
view.setName("DEFAULT");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// for any views in the report, if they don't specify a data source name, //
|
||||
// but there's only 1 data source, then use that single data source's name //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
for(QReportView view : report.getViews())
|
||||
{
|
||||
if(!StringUtils.hasContent(view.getDataSourceName()) && singleDataSourceName != null)
|
||||
{
|
||||
view.setDataSourceName(singleDataSourceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
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.metadata.QInstance;
|
||||
|
||||
|
||||
@ -39,6 +40,8 @@ public class AggregateInput extends AbstractTableActionInput
|
||||
private List<Aggregate> aggregates;
|
||||
private List<String> groupByFieldNames;
|
||||
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -192,4 +195,54 @@ public class AggregateInput extends AbstractTableActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QueryJoin> getQueryJoins()
|
||||
{
|
||||
return queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AggregateInput withQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AggregateInput withQueryJoin(QueryJoin queryJoin)
|
||||
{
|
||||
if(this.queryJoins == null)
|
||||
{
|
||||
this.queryJoins = new ArrayList<>();
|
||||
}
|
||||
this.queryJoins.add(queryJoin);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,8 +22,11 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.count;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
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.metadata.QInstance;
|
||||
|
||||
|
||||
@ -35,6 +38,8 @@ public class CountInput extends AbstractTableActionInput
|
||||
{
|
||||
private QQueryFilter filter;
|
||||
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -78,4 +83,52 @@ public class CountInput extends AbstractTableActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QueryJoin> getQueryJoins()
|
||||
{
|
||||
return queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public CountInput withQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public CountInput withQueryJoin(QueryJoin queryJoin)
|
||||
{
|
||||
if(this.queryJoins == null)
|
||||
{
|
||||
this.queryJoins = new ArrayList<>();
|
||||
}
|
||||
this.queryJoins.add(queryJoin);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.query;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper object used throughout query (and related (count, aggregate, reporting))
|
||||
** actions that need to track joins and aliases.
|
||||
*******************************************************************************/
|
||||
public class JoinsContext
|
||||
{
|
||||
private final QInstance instance;
|
||||
private final String mainTableName;
|
||||
private final List<QueryJoin> queryJoins;
|
||||
private final Map<String, String> aliasToTableNameMap = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public JoinsContext(QInstance instance, String tableName, List<QueryJoin> queryJoins) throws QException
|
||||
{
|
||||
this.instance = instance;
|
||||
this.mainTableName = tableName;
|
||||
this.queryJoins = CollectionUtils.nonNullList(queryJoins);
|
||||
|
||||
for(QueryJoin queryJoin : this.queryJoins)
|
||||
{
|
||||
QTableMetaData joinTable = instance.getTable(queryJoin.getRightTable());
|
||||
String tableNameOrAlias = queryJoin.getAliasOrRightTable();
|
||||
if(aliasToTableNameMap.containsKey(tableNameOrAlias))
|
||||
{
|
||||
throw (new QException("Duplicate table name or alias: " + tableNameOrAlias));
|
||||
}
|
||||
aliasToTableNameMap.put(tableNameOrAlias, joinTable.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QueryJoin> getQueryJoins()
|
||||
{
|
||||
return queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String resolveTableNameOrAliasToTableName(String nameOrAlias)
|
||||
{
|
||||
if(aliasToTableNameMap.containsKey(nameOrAlias))
|
||||
{
|
||||
return (aliasToTableNameMap.get(nameOrAlias));
|
||||
}
|
||||
return (nameOrAlias);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public FieldAndTableNameOrAlias getFieldAndTableNameOrAlias(String fieldName)
|
||||
{
|
||||
if(fieldName.contains("."))
|
||||
{
|
||||
String[] parts = fieldName.split("\\.");
|
||||
if(parts.length != 2)
|
||||
{
|
||||
throw new IllegalArgumentException("Mal-formatted field name in query: " + fieldName);
|
||||
}
|
||||
|
||||
String tableOrAlias = parts[0];
|
||||
String baseFieldName = parts[1];
|
||||
String tableName = resolveTableNameOrAliasToTableName(tableOrAlias);
|
||||
|
||||
QTableMetaData table = instance.getTable(tableName);
|
||||
if(table == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Could not find table [" + tableName + "] in instance for query");
|
||||
}
|
||||
return new FieldAndTableNameOrAlias(table.getField(baseFieldName), tableOrAlias);
|
||||
}
|
||||
|
||||
return new FieldAndTableNameOrAlias(instance.getTable(mainTableName).getField(fieldName), mainTableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public record FieldAndTableNameOrAlias(QFieldMetaData field, String tableNameOrAlias)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@ -43,6 +44,8 @@ public class QFilterCriteria implements Serializable, Cloneable
|
||||
private QCriteriaOperator operator;
|
||||
private List<Serializable> values;
|
||||
|
||||
private String otherFieldName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -220,6 +223,40 @@ public class QFilterCriteria implements Serializable, Cloneable
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for otherFieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getOtherFieldName()
|
||||
{
|
||||
return otherFieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for otherFieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setOtherFieldName(String otherFieldName)
|
||||
{
|
||||
this.otherFieldName = otherFieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for otherFieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFilterCriteria withOtherFieldName(String otherFieldName)
|
||||
{
|
||||
this.otherFieldName = otherFieldName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -232,21 +269,28 @@ public class QFilterCriteria implements Serializable, Cloneable
|
||||
rs.append(" ").append(operator).append(" ");
|
||||
if(CollectionUtils.nullSafeHasContents(values))
|
||||
{
|
||||
if(values.size() == 1)
|
||||
if(StringUtils.hasContent(otherFieldName))
|
||||
{
|
||||
rs.append(values.get(0));
|
||||
rs.append(otherFieldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = 0;
|
||||
for(Serializable value : values)
|
||||
if(values.size() == 1)
|
||||
{
|
||||
if(index++ > 9)
|
||||
rs.append(values.get(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = 0;
|
||||
for(Serializable value : values)
|
||||
{
|
||||
rs.append("and ").append(values.size() - index).append(" more");
|
||||
break;
|
||||
if(index++ > 9)
|
||||
{
|
||||
rs.append("and ").append(values.size() - index).append(" more");
|
||||
break;
|
||||
}
|
||||
rs.append(value).append(",");
|
||||
}
|
||||
rs.append(value).append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
@ -45,6 +47,8 @@ public class QueryInput extends AbstractTableActionInput
|
||||
private boolean shouldTranslatePossibleValues = false;
|
||||
private boolean shouldGenerateDisplayValues = false;
|
||||
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -241,4 +245,54 @@ public class QueryInput extends AbstractTableActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QueryJoin> getQueryJoins()
|
||||
{
|
||||
return queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryInput withQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryInput withQueryJoin(QueryJoin queryJoin)
|
||||
{
|
||||
if(this.queryJoins == null)
|
||||
{
|
||||
this.queryJoins = new ArrayList<>();
|
||||
}
|
||||
this.queryJoins.add(queryJoin);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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.query;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Part of query (or count, aggregate) input, to do a Join as part of a query.
|
||||
*******************************************************************************/
|
||||
public class QueryJoin
|
||||
{
|
||||
private String leftTableOrAlias;
|
||||
private String rightTable;
|
||||
private QJoinMetaData joinMetaData;
|
||||
private String alias;
|
||||
private boolean select = false;
|
||||
private Type type = Type.INNER;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum Type
|
||||
{INNER, LEFT, RIGHT, FULL}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin(String leftTableOrAlias, String rightTable)
|
||||
{
|
||||
this.leftTableOrAlias = leftTableOrAlias;
|
||||
this.rightTable = rightTable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin(QJoinMetaData joinMetaData)
|
||||
{
|
||||
setJoinMetaData(joinMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for leftTableOrAlias
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getLeftTableOrAlias()
|
||||
{
|
||||
return leftTableOrAlias;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for leftTableOrAlias
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setLeftTableOrAlias(String leftTableOrAlias)
|
||||
{
|
||||
this.leftTableOrAlias = leftTableOrAlias;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for leftTableOrAlias
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin withLeftTableOrAlias(String leftTableOrAlias)
|
||||
{
|
||||
this.leftTableOrAlias = leftTableOrAlias;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rightTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getRightTable()
|
||||
{
|
||||
return rightTable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rightTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRightTable(String rightTable)
|
||||
{
|
||||
this.rightTable = rightTable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rightTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin withRightTable(String rightTable)
|
||||
{
|
||||
this.rightTable = rightTable;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for alias
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getAlias()
|
||||
{
|
||||
return alias;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for alias
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setAlias(String alias)
|
||||
{
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for alias
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin withAlias(String alias)
|
||||
{
|
||||
this.alias = alias;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for select
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getSelect()
|
||||
{
|
||||
return select;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for select
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSelect(boolean select)
|
||||
{
|
||||
this.select = select;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for select
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin withSelect(boolean select)
|
||||
{
|
||||
this.select = select;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getAliasOrRightTable()
|
||||
{
|
||||
if(StringUtils.hasContent(alias))
|
||||
{
|
||||
return (alias);
|
||||
}
|
||||
return (rightTable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for type
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Type getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for type
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setType(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for type
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin withType(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for joinMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QJoinMetaData getJoinMetaData()
|
||||
{
|
||||
return joinMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for joinMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setJoinMetaData(QJoinMetaData joinMetaData)
|
||||
{
|
||||
this.joinMetaData = joinMetaData;
|
||||
|
||||
if(!StringUtils.hasContent(this.leftTableOrAlias) && !StringUtils.hasContent(this.rightTable))
|
||||
{
|
||||
setLeftTableOrAlias(joinMetaData.getLeftTable());
|
||||
setRightTable(joinMetaData.getRightTable());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for joinMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryJoin withJoinMetaData(QJoinMetaData joinMetaData)
|
||||
{
|
||||
setJoinMetaData(joinMetaData);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.joins;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Specification for (at least part of) how two tables join together - e.g.,
|
||||
** leftField = rightField. Used as part of a list in a QJoinMetaData.
|
||||
*******************************************************************************/
|
||||
public class JoinOn
|
||||
{
|
||||
@ -54,6 +55,16 @@ public class JoinOn
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Return a new JoinOn, with the fields of this one, but flipped (right ←→ left)
|
||||
*******************************************************************************/
|
||||
public JoinOn flip()
|
||||
{
|
||||
return new JoinOn(rightField, leftField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for leftField
|
||||
**
|
||||
|
@ -25,7 +25,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.joins;
|
||||
/*******************************************************************************
|
||||
** Type for a QJoin.
|
||||
**
|
||||
** - One to One - what about zero??
|
||||
** - One to One - or zero, i guess...
|
||||
** - One to Many - e.g., where the parent record really "owns" all of the child
|
||||
** records. Like Order -> OrderLine.
|
||||
** - Many to One - e.g., where a child references a parent, but we'd never really
|
||||
@ -37,5 +37,21 @@ public enum JoinType
|
||||
ONE_TO_ONE,
|
||||
ONE_TO_MANY,
|
||||
MANY_TO_ONE,
|
||||
MANY_TO_MANY
|
||||
MANY_TO_MANY;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
public JoinType flip()
|
||||
{
|
||||
return switch(this)
|
||||
{
|
||||
case ONE_TO_MANY -> MANY_TO_ONE;
|
||||
case MANY_TO_ONE -> ONE_TO_MANY;
|
||||
case MANY_TO_MANY, ONE_TO_ONE -> this;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Definition of how 2 tables join together within a QQQ Instance.
|
||||
*******************************************************************************/
|
||||
public class QJoinMetaData
|
||||
{
|
||||
@ -43,6 +43,22 @@ public class QJoinMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Return a new QJoinMetaData, with all the same data as this one, but flipped
|
||||
** right ←→ left, for the tables, the type, and the joinOns.
|
||||
*******************************************************************************/
|
||||
public QJoinMetaData flip()
|
||||
{
|
||||
return (new QJoinMetaData()
|
||||
.withLeftTable(rightTable)
|
||||
.withRightTable(leftTable)
|
||||
.withType(type.flip())
|
||||
.withJoinOns(joinOns.stream().map(JoinOn::flip).toList()));
|
||||
// todo - what about order bys??
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
|
@ -22,7 +22,10 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.reporting;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
@ -32,9 +35,12 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
*******************************************************************************/
|
||||
public class QReportDataSource
|
||||
{
|
||||
private String name;
|
||||
private String sourceTable;
|
||||
private QQueryFilter queryFilter;
|
||||
private String name;
|
||||
|
||||
private String sourceTable;
|
||||
private QQueryFilter queryFilter;
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
|
||||
|
||||
private QCodeReference staticDataSupplier;
|
||||
|
||||
@ -174,4 +180,54 @@ public class QReportDataSource
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QueryJoin> getQueryJoins()
|
||||
{
|
||||
return queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QReportDataSource withQueryJoins(List<QueryJoin> queryJoins)
|
||||
{
|
||||
this.queryJoins = queryJoins;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryJoins
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QReportDataSource withQueryJoin(QueryJoin queryJoin)
|
||||
{
|
||||
if(this.queryJoins == null)
|
||||
{
|
||||
this.queryJoins = new ArrayList<>();
|
||||
}
|
||||
this.queryJoins.add(queryJoin);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -45,6 +45,27 @@ public class QReportField
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QReportField()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QReportField(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.reporting;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
@ -150,6 +151,21 @@ public class QReportMetaData implements QAppChildMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QReportMetaData withInputField(QFieldMetaData inputField)
|
||||
{
|
||||
if(this.inputFields == null)
|
||||
{
|
||||
this.inputFields = new ArrayList<>();
|
||||
}
|
||||
this.inputFields.add(inputField);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processName
|
||||
**
|
||||
@ -218,6 +234,22 @@ public class QReportMetaData implements QAppChildMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for dataSources
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QReportMetaData withDataSource(QReportDataSource dataSource)
|
||||
{
|
||||
if(this.dataSources == null)
|
||||
{
|
||||
this.dataSources = new ArrayList<>();
|
||||
}
|
||||
this.dataSources.add(dataSource);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for views
|
||||
**
|
||||
@ -252,6 +284,22 @@ public class QReportMetaData implements QAppChildMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for views
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QReportMetaData withView(QReportView view)
|
||||
{
|
||||
if(this.views == null)
|
||||
{
|
||||
this.views = new ArrayList<>();
|
||||
}
|
||||
this.views.add(view);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMeta
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -63,6 +64,21 @@ public class RunReportForRecordProcess
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Create a process meta data builder for this type of process, pre-populated
|
||||
** with attributes based on a given report.
|
||||
*******************************************************************************/
|
||||
public static Builder processMetaDataBuilder(QReportMetaData reportMetaData)
|
||||
{
|
||||
return (new Builder(defineProcessMetaData())
|
||||
.withProcessName(reportMetaData.getProcessName())
|
||||
.withReportName(reportMetaData.getName())
|
||||
.withTableName(reportMetaData.getDataSources().get(0).getSourceTable())
|
||||
.withIcon(reportMetaData.getIcon()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -55,6 +55,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleVal
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
@ -1344,11 +1345,35 @@ class QInstanceValidatorTest
|
||||
@Test
|
||||
void testReportDataSourceNames()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setName(null),
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// enricher will give us a default name if only 1 data source, so, set 1st one to null name, then add a second //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QReportMetaData report = qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON);
|
||||
report.setDataSources(new ArrayList<>(report.getDataSources()));
|
||||
report.getDataSources().get(0).setName(null);
|
||||
report.getDataSources().add(new QReportDataSource()
|
||||
.withName("2nd")
|
||||
.withSourceTable(TestUtils.TABLE_NAME_PERSON)
|
||||
);
|
||||
},
|
||||
"Missing name for a dataSource",
|
||||
"unrecognized dataSourceName");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setName(""),
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
///////////////////////////////////
|
||||
// same as above, but "" vs null //
|
||||
///////////////////////////////////
|
||||
QReportMetaData report = qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON);
|
||||
report.setDataSources(new ArrayList<>(report.getDataSources()));
|
||||
report.getDataSources().get(0).setName("");
|
||||
report.getDataSources().add(new QReportDataSource()
|
||||
.withName("2nd")
|
||||
.withSourceTable(TestUtils.TABLE_NAME_PERSON)
|
||||
);
|
||||
},
|
||||
"Missing name for a dataSource",
|
||||
"unrecognized dataSourceName");
|
||||
|
||||
|
Reference in New Issue
Block a user