diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/QueryStatManager.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/QueryStatManager.java
new file mode 100644
index 00000000..59ee1db6
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/QueryStatManager.java
@@ -0,0 +1,410 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2023. 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 .
+ */
+
+package com.kingsrook.qqq.backend.core.actions.tables.helpers;
+
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
+import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
+import com.kingsrook.qqq.backend.core.context.QContext;
+import com.kingsrook.qqq.backend.core.exceptions.QException;
+import com.kingsrook.qqq.backend.core.logging.QLogger;
+import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
+import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
+import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
+import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
+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.data.QRecord;
+import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
+import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
+import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
+import com.kingsrook.qqq.backend.core.model.querystats.QueryStatCriteriaField;
+import com.kingsrook.qqq.backend.core.model.querystats.QueryStatJoinTable;
+import com.kingsrook.qqq.backend.core.model.session.QSession;
+import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
+import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
+import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
+import com.kingsrook.qqq.backend.core.utils.StringUtils;
+import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
+import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
+
+
+/*******************************************************************************
+ **
+ *******************************************************************************/
+public class QueryStatManager
+{
+ private static QueryStatManager queryStatManager = null;
+
+ // todo - support multiple qInstances?
+ private QInstance qInstance;
+ private Supplier sessionSupplier;
+
+ private boolean active = false;
+ private List queryStats = new ArrayList<>();
+
+ private ScheduledExecutorService executorService;
+
+
+
+ /*******************************************************************************
+ ** Singleton constructor
+ *******************************************************************************/
+ private QueryStatManager()
+ {
+
+ }
+
+
+
+ /*******************************************************************************
+ ** Singleton accessor
+ *******************************************************************************/
+ public static QueryStatManager getInstance()
+ {
+ if(queryStatManager == null)
+ {
+ queryStatManager = new QueryStatManager();
+ }
+ return (queryStatManager);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public void start(QInstance qInstance, Supplier sessionSupplier)
+ {
+ this.qInstance = qInstance;
+ this.sessionSupplier = sessionSupplier;
+
+ active = true;
+ queryStats = new ArrayList<>();
+
+ executorService = Executors.newSingleThreadScheduledExecutor();
+ executorService.scheduleAtFixedRate(new QueryStatManagerInsertJob(), 6, 6, TimeUnit.SECONDS); // todo - 60s
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public void stop()
+ {
+ active = false;
+ queryStats.clear();
+
+ if(executorService != null)
+ {
+ executorService.shutdown();
+ executorService = null;
+ }
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public void add(QueryStat queryStat)
+ {
+ if(active)
+ {
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // set fields that we need to capture now (rather than when the thread to store runs) //
+ ////////////////////////////////////////////////////////////////////////////////////////
+ if(queryStat.getFirstResultTimestamp() == null)
+ {
+ queryStat.setFirstResultTimestamp(Instant.now());
+ }
+
+ if(queryStat.getSessionId() == null && QContext.getQSession() != null)
+ {
+ queryStat.setSessionId(QContext.getQSession().getUuid());
+ }
+
+ if(queryStat.getAction() == null)
+ {
+ if(!QContext.getActionStack().isEmpty())
+ {
+ queryStat.setAction(QContext.getActionStack().peek().getActionIdentity());
+ }
+ else
+ {
+ boolean expected = false;
+ Exception e = new Exception("Unexpected empty action stack");
+ for(StackTraceElement stackTraceElement : e.getStackTrace())
+ {
+ String className = stackTraceElement.getClassName();
+ if(className.contains(QueryStatManagerInsertJob.class.getName()))
+ {
+ expected = true;
+ }
+ }
+
+ if(!expected)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ synchronized(this)
+ {
+ queryStats.add(queryStat);
+ }
+ }
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private List getListAndReset()
+ {
+ if(queryStats.isEmpty())
+ {
+ return Collections.emptyList();
+ }
+
+ synchronized(this)
+ {
+ List returnList = queryStats;
+ queryStats = new ArrayList<>();
+ return (returnList);
+ }
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public void storeStatsNow()
+ {
+ new QueryStatManagerInsertJob().run();
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private static class QueryStatManagerInsertJob implements Runnable
+ {
+ private static final QLogger LOG = QLogger.getLogger(QueryStatManagerInsertJob.class);
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Override
+ public void run()
+ {
+ try
+ {
+ QContext.init(getInstance().qInstance, getInstance().sessionSupplier.get());
+
+ List list = getInstance().getListAndReset();
+ LOG.info(logPair("queryStatListSize", list.size()));
+
+ if(list.isEmpty())
+ {
+ return;
+ }
+
+ ////////////////////////////////////
+ // prime the entities for storing //
+ ////////////////////////////////////
+ List queryStatQRecordsToInsert = new ArrayList<>();
+ for(QueryStat queryStat : list)
+ {
+ try
+ {
+ ///////////////////////////////////////////////
+ // compute the millis (so you don't have to) //
+ ///////////////////////////////////////////////
+ if(queryStat.getStartTimestamp() != null && queryStat.getFirstResultTimestamp() != null && queryStat.getFirstResultMillis() == null)
+ {
+ long millis = queryStat.getFirstResultTimestamp().toEpochMilli() - queryStat.getStartTimestamp().toEpochMilli();
+ queryStat.setFirstResultMillis((int) millis);
+ }
+
+ //////////////////////
+ // set the table id //
+ //////////////////////
+ Integer qqqTableId = getQQQTableId(queryStat.getTableName());
+ queryStat.setQqqTableId(qqqTableId);
+
+ //////////////////////////////
+ // build join-table records //
+ //////////////////////////////
+ if(CollectionUtils.nullSafeHasContents(queryStat.getJoinTableNames()))
+ {
+ List queryStatJoinTableList = new ArrayList<>();
+ for(String joinTableName : queryStat.getJoinTableNames())
+ {
+ queryStatJoinTableList.add(new QueryStatJoinTable().withQqqTableId(getQQQTableId(joinTableName)));
+ }
+ queryStat.setQueryStatJoinTableList(queryStatJoinTableList);
+ }
+
+ ////////////////////////////
+ // build criteria records //
+ ////////////////////////////
+ if(queryStat.getQueryFilter() != null && queryStat.getQueryFilter().hasAnyCriteria())
+ {
+ List queryStatCriteriaFieldList = new ArrayList<>();
+ QQueryFilter queryFilter = queryStat.getQueryFilter();
+ processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, queryFilter);
+ queryStat.setQueryStatCriteriaFieldList(queryStatCriteriaFieldList);
+ }
+
+ // todo - orderbys
+
+ queryStatQRecordsToInsert.add(queryStat.toQRecord());
+ }
+ catch(Exception e)
+ {
+ //////////////////////
+ // skip this record //
+ //////////////////////
+ LOG.warn("Error priming a query stat for storing", e);
+ }
+ }
+
+ try
+ {
+ InsertInput insertInput = new InsertInput();
+ insertInput.setTableName(QueryStat.TABLE_NAME);
+ insertInput.setRecords(queryStatQRecordsToInsert);
+ new InsertAction().execute(insertInput);
+ }
+ catch(Exception e)
+ {
+ LOG.error("Error inserting query stats", e);
+ }
+ }
+ catch(Exception e)
+ {
+ LOG.warn("Error storing query stats", e);
+ }
+ finally
+ {
+ QContext.clear();
+ }
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private static void processCriteriaFromFilter(Integer qqqTableId, List queryStatCriteriaFieldList, QQueryFilter queryFilter) throws QException
+ {
+ for(QFilterCriteria criteria : CollectionUtils.nonNullList(queryFilter.getCriteria()))
+ {
+ String fieldName = criteria.getFieldName();
+ QueryStatCriteriaField queryStatCriteriaField = new QueryStatCriteriaField();
+ queryStatCriteriaField.setOperator(String.valueOf(criteria.getOperator()));
+
+ if(criteria.getValues() != null)
+ {
+ queryStatCriteriaField.setValues(StringUtils.join(",", criteria.getValues()));
+ }
+
+ if(fieldName.contains("."))
+ {
+ String[] parts = fieldName.split("\\.");
+ if(parts.length > 1)
+ {
+ queryStatCriteriaField.setQqqTableId(getQQQTableId(parts[0]));
+ queryStatCriteriaField.setName(parts[1]);
+ }
+ }
+ else
+ {
+ queryStatCriteriaField.setQqqTableId(qqqTableId);
+ queryStatCriteriaField.setName(fieldName);
+ }
+
+ queryStatCriteriaFieldList.add(queryStatCriteriaField);
+ }
+
+ for(QQueryFilter subFilter : CollectionUtils.nonNullList(queryFilter.getSubFilters()))
+ {
+ processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, subFilter);
+ }
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private static Integer getQQQTableId(String tableName) throws QException
+ {
+ /////////////////////////////
+ // look in the cache table //
+ /////////////////////////////
+ GetInput getInput = new GetInput();
+ getInput.setTableName(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME);
+ getInput.setUniqueKey(MapBuilder.of("name", tableName));
+ GetOutput getOutput = new GetAction().execute(getInput);
+
+ ////////////////////////
+ // upon cache miss... //
+ ////////////////////////
+ if(getOutput.getRecord() == null)
+ {
+ ///////////////////////////////////////////////////////
+ // insert the record (into the table, not the cache) //
+ ///////////////////////////////////////////////////////
+ QTableMetaData tableMetaData = getInstance().qInstance.getTable(tableName);
+ InsertInput insertInput = new InsertInput();
+ insertInput.setTableName(QQQTable.TABLE_NAME);
+ insertInput.setRecords(List.of(new QRecord().withValue("name", tableName).withValue("label", tableMetaData.getLabel())));
+ InsertOutput insertOutput = new InsertAction().execute(insertInput);
+
+ ///////////////////////////////////
+ // repeat the get from the cache //
+ ///////////////////////////////////
+ getOutput = new GetAction().execute(getInput);
+ }
+
+ return getOutput.getRecord().getValueInteger("id");
+ }
+ }
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStat.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStat.java
similarity index 50%
rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStat.java
rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStat.java
index 2c35f9f4..a0e5f513 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStat.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStat.java
@@ -19,37 +19,31 @@
* along with this program. If not, see .
*/
-package com.kingsrook.qqq.backend.core.actions.tables.helpers.querystats;
+package com.kingsrook.qqq.backend.core.model.querystats;
import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
-import java.util.stream.Collectors;
-import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
+import java.util.Set;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QAssociation;
import com.kingsrook.qqq.backend.core.model.data.QField;
+import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
-import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
-import com.kingsrook.qqq.backend.core.utils.ValueUtils;
+import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
/*******************************************************************************
- **
+ ** QRecord Entity for QueryStat table
*******************************************************************************/
public class QueryStat extends QRecordEntity
{
public static final String TABLE_NAME = "queryStat";
- @QField()
+ @QField(isEditable = false)
private Integer id;
- @QField()
- private String tableName;
-
@QField()
private Instant startTimestamp;
@@ -59,121 +53,81 @@ public class QueryStat extends QRecordEntity
@QField()
private Integer firstResultMillis;
- @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE)
- private String joinTables;
+ @QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
+ private Integer qqqTableId;
- @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE)
- private String orderBys;
+ @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String action;
- @QAssociation(name = "queryStatFilterCriteria")
- private List queryStatFilterCriteriaList;
+ @QField(maxLength = 36, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String sessionId;
+
+ @QField(maxLength = 64 * 1024 - 1, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String queryText;
+
+ @QAssociation(name = "queryStatJoinTables")
+ private List queryStatJoinTableList;
+
+ @QAssociation(name = "queryStatCriteriaFields")
+ private List queryStatCriteriaFieldList;
+
+ @QAssociation(name = "queryStatOrderByFields")
+ private List queryStatOrderByFieldList;
+
+ ///////////////////////////////////////////////////////////
+ // non-persistent fields - used to help build the record //
+ ///////////////////////////////////////////////////////////
+ private String tableName;
+ private Set joinTableNames;
+ private QQueryFilter queryFilter;
/*******************************************************************************
- **
+ ** Default constructor
*******************************************************************************/
- public void setJoinTables(Collection joinTableNames)
+ public QueryStat()
{
- if(CollectionUtils.nullSafeIsEmpty(joinTableNames))
- {
- setJoinTables((String) null);
- }
-
- setJoinTables(joinTableNames.stream().sorted().collect(Collectors.joining(",")));
}
/*******************************************************************************
- **
+ ** Constructor that takes a QRecord
*******************************************************************************/
- public void setQQueryFilter(QQueryFilter filter)
+ public QueryStat(QRecord record)
{
- if(filter == null)
- {
- setQueryStatFilterCriteriaList(null);
- setOrderBys(null);
- }
- else
- {
- /////////////////////////////////////////////
- // manage list of sub-records for criteria //
- /////////////////////////////////////////////
- if(CollectionUtils.nullSafeIsEmpty(filter.getCriteria()) && CollectionUtils.nullSafeIsEmpty(filter.getSubFilters()))
- {
- setQueryStatFilterCriteriaList(null);
- }
- else
- {
- ArrayList criteriaList = new ArrayList<>();
- setQueryStatFilterCriteriaList(criteriaList);
- processFilterCriteria(filter, criteriaList);
- }
-
- //////////////////////////////////////////////////////////////
- // set orderBys (comma-delimited concatenated string field) //
- //////////////////////////////////////////////////////////////
- if(CollectionUtils.nullSafeIsEmpty(filter.getOrderBys()))
- {
- setOrderBys(null);
- }
- else
- {
- setOrderBys(filter.getOrderBys().stream().map(ob -> ob.getFieldName()).collect(Collectors.joining(",")));
- }
- }
+ populateFromQRecord(record);
}
/*******************************************************************************
- **
+ ** Getter for id
*******************************************************************************/
- private static void processFilterCriteria(QQueryFilter filter, ArrayList criteriaList)
+ public Integer getId()
{
- for(QFilterCriteria criterion : CollectionUtils.nonNullList(filter.getCriteria()))
- {
- criteriaList.add(new QueryStatFilterCriteria()
- .withFieldName(criterion.getFieldName())
- .withOperator(criterion.getOperator().name())
- .withValues(CollectionUtils.nonNullList(criterion.getValues()).stream().map(v -> ValueUtils.getValueAsString(v)).collect(Collectors.joining(","))));
- }
-
- for(QQueryFilter subFilter : CollectionUtils.nonNullList(filter.getSubFilters()))
- {
- processFilterCriteria(subFilter, criteriaList);
- }
+ return (this.id);
}
/*******************************************************************************
- ** Getter for tableName
+ ** Setter for id
*******************************************************************************/
- public String getTableName()
+ public void setId(Integer id)
{
- return (this.tableName);
+ this.id = id;
}
/*******************************************************************************
- ** Setter for tableName
+ ** Fluent setter for id
*******************************************************************************/
- public void setTableName(String tableName)
+ public QueryStat withId(Integer id)
{
- this.tableName = tableName;
- }
-
-
-
- /*******************************************************************************
- ** Fluent setter for tableName
- *******************************************************************************/
- public QueryStat withTableName(String tableName)
- {
- this.tableName = tableName;
+ this.id = id;
return (this);
}
@@ -273,124 +227,310 @@ public class QueryStat extends QRecordEntity
/*******************************************************************************
- ** Getter for joinTables
+ ** Getter for queryText
*******************************************************************************/
- public String getJoinTables()
+ public String getQueryText()
{
- return (this.joinTables);
+ return (this.queryText);
}
/*******************************************************************************
- ** Setter for joinTables
+ ** Setter for queryText
*******************************************************************************/
- public void setJoinTables(String joinTables)
+ public void setQueryText(String queryText)
{
- this.joinTables = joinTables;
+ this.queryText = queryText;
}
/*******************************************************************************
- ** Fluent setter for joinTables
+ ** Fluent setter for queryText
*******************************************************************************/
- public QueryStat withJoinTables(String joinTables)
+ public QueryStat withQueryText(String queryText)
{
- this.joinTables = joinTables;
+ this.queryText = queryText;
return (this);
}
/*******************************************************************************
- ** Getter for queryStatFilterCriteriaList
+ ** Getter for queryStatJoinTableList
*******************************************************************************/
- public List getQueryStatFilterCriteriaList()
+ public List getQueryStatJoinTableList()
{
- return (this.queryStatFilterCriteriaList);
+ return (this.queryStatJoinTableList);
}
/*******************************************************************************
- ** Setter for queryStatFilterCriteriaList
+ ** Setter for queryStatJoinTableList
*******************************************************************************/
- public void setQueryStatFilterCriteriaList(List queryStatFilterCriteriaList)
+ public void setQueryStatJoinTableList(List queryStatJoinTableList)
{
- this.queryStatFilterCriteriaList = queryStatFilterCriteriaList;
+ this.queryStatJoinTableList = queryStatJoinTableList;
}
/*******************************************************************************
- ** Fluent setter for queryStatFilterCriteriaList
+ ** Fluent setter for queryStatJoinTableList
*******************************************************************************/
- public QueryStat withQueryStatFilterCriteriaList(List queryStatFilterCriteriaList)
+ public QueryStat withQueryStatJoinTableList(List queryStatJoinTableList)
{
- this.queryStatFilterCriteriaList = queryStatFilterCriteriaList;
+ this.queryStatJoinTableList = queryStatJoinTableList;
return (this);
}
/*******************************************************************************
- ** Getter for orderBys
+ ** Getter for queryStatCriteriaFieldList
*******************************************************************************/
- public String getOrderBys()
+ public List getQueryStatCriteriaFieldList()
{
- return (this.orderBys);
+ return (this.queryStatCriteriaFieldList);
}
/*******************************************************************************
- ** Setter for orderBys
+ ** Setter for queryStatCriteriaFieldList
*******************************************************************************/
- public void setOrderBys(String orderBys)
+ public void setQueryStatCriteriaFieldList(List queryStatCriteriaFieldList)
{
- this.orderBys = orderBys;
+ this.queryStatCriteriaFieldList = queryStatCriteriaFieldList;
}
/*******************************************************************************
- ** Fluent setter for orderBys
+ ** Fluent setter for queryStatCriteriaFieldList
*******************************************************************************/
- public QueryStat withOrderBys(String orderBys)
+ public QueryStat withQueryStatCriteriaFieldList(List queryStatCriteriaFieldList)
{
- this.orderBys = orderBys;
+ this.queryStatCriteriaFieldList = queryStatCriteriaFieldList;
return (this);
}
/*******************************************************************************
- ** Getter for id
+ ** Getter for queryStatOrderByFieldList
*******************************************************************************/
- public Integer getId()
+ public List getQueryStatOrderByFieldList()
{
- return (this.id);
+ return (this.queryStatOrderByFieldList);
}
/*******************************************************************************
- ** Setter for id
+ ** Setter for queryStatOrderByFieldList
*******************************************************************************/
- public void setId(Integer id)
+ public void setQueryStatOrderByFieldList(List queryStatOrderByFieldList)
{
- this.id = id;
+ this.queryStatOrderByFieldList = queryStatOrderByFieldList;
}
/*******************************************************************************
- ** Fluent setter for id
+ ** Fluent setter for queryStatOrderByFieldList
*******************************************************************************/
- public QueryStat withId(Integer id)
+ public QueryStat withQueryStatOrderByFieldList(List queryStatOrderByFieldList)
{
- this.id = id;
+ this.queryStatOrderByFieldList = queryStatOrderByFieldList;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for tableName
+ *******************************************************************************/
+ public String getTableName()
+ {
+ return (this.tableName);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for tableName
+ *******************************************************************************/
+ public void setTableName(String tableName)
+ {
+ this.tableName = tableName;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for tableName
+ *******************************************************************************/
+ public QueryStat withTableName(String tableName)
+ {
+ this.tableName = tableName;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for queryFilter
+ *******************************************************************************/
+ public QQueryFilter getQueryFilter()
+ {
+ return (this.queryFilter);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for queryFilter
+ *******************************************************************************/
+ public void setQueryFilter(QQueryFilter queryFilter)
+ {
+ this.queryFilter = queryFilter;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for queryFilter
+ *******************************************************************************/
+ public QueryStat withQueryFilter(QQueryFilter queryFilter)
+ {
+ this.queryFilter = queryFilter;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for qqqTableId
+ *******************************************************************************/
+ public Integer getQqqTableId()
+ {
+ return (this.qqqTableId);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for qqqTableId
+ *******************************************************************************/
+ public void setQqqTableId(Integer qqqTableId)
+ {
+ this.qqqTableId = qqqTableId;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for qqqTableId
+ *******************************************************************************/
+ public QueryStat withQqqTableId(Integer qqqTableId)
+ {
+ this.qqqTableId = qqqTableId;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for joinTableNames
+ *******************************************************************************/
+ public Set getJoinTableNames()
+ {
+ return (this.joinTableNames);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for joinTableNames
+ *******************************************************************************/
+ public void setJoinTableNames(Set joinTableNames)
+ {
+ this.joinTableNames = joinTableNames;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for joinTableNames
+ *******************************************************************************/
+ public QueryStat withJoinTableNames(Set joinTableNames)
+ {
+ this.joinTableNames = joinTableNames;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for action
+ *******************************************************************************/
+ public String getAction()
+ {
+ return (this.action);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for action
+ *******************************************************************************/
+ public void setAction(String action)
+ {
+ this.action = action;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for action
+ *******************************************************************************/
+ public QueryStat withAction(String action)
+ {
+ this.action = action;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for sessionId
+ *******************************************************************************/
+ public String getSessionId()
+ {
+ return (this.sessionId);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for sessionId
+ *******************************************************************************/
+ public void setSessionId(String sessionId)
+ {
+ this.sessionId = sessionId;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for sessionId
+ *******************************************************************************/
+ public QueryStat withSessionId(String sessionId)
+ {
+ this.sessionId = sessionId;
return (this);
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatCriteriaField.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatCriteriaField.java
new file mode 100644
index 00000000..bd25a51f
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatCriteriaField.java
@@ -0,0 +1,262 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2023. 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 .
+ */
+
+package com.kingsrook.qqq.backend.core.model.querystats;
+
+
+import com.kingsrook.qqq.backend.core.model.data.QField;
+import com.kingsrook.qqq.backend.core.model.data.QRecord;
+import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
+import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
+import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
+
+
+/*******************************************************************************
+ ** QRecord Entity for QueryStatCriteriaField table
+ *******************************************************************************/
+public class QueryStatCriteriaField extends QRecordEntity
+{
+ public static final String TABLE_NAME = "queryStatCriteriaField";
+
+ @QField(isEditable = false)
+ private Integer id;
+
+ @QField(possibleValueSourceName = QueryStat.TABLE_NAME)
+ private Integer queryStatId;
+
+ @QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
+ private Integer qqqTableId;
+
+ @QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String name;
+
+ @QField(maxLength = 30, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String operator;
+
+ @QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String values;
+
+
+
+ /*******************************************************************************
+ ** Default constructor
+ *******************************************************************************/
+ public QueryStatCriteriaField()
+ {
+ }
+
+
+
+ /*******************************************************************************
+ ** Constructor that takes a QRecord
+ *******************************************************************************/
+ public QueryStatCriteriaField(QRecord record)
+ {
+ populateFromQRecord(record);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for id
+ *******************************************************************************/
+ public Integer getId()
+ {
+ return (this.id);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for id
+ *******************************************************************************/
+ public void setId(Integer id)
+ {
+ this.id = id;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for id
+ *******************************************************************************/
+ public QueryStatCriteriaField withId(Integer id)
+ {
+ this.id = id;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for queryStatId
+ *******************************************************************************/
+ public Integer getQueryStatId()
+ {
+ return (this.queryStatId);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for queryStatId
+ *******************************************************************************/
+ public void setQueryStatId(Integer queryStatId)
+ {
+ this.queryStatId = queryStatId;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for queryStatId
+ *******************************************************************************/
+ public QueryStatCriteriaField withQueryStatId(Integer queryStatId)
+ {
+ this.queryStatId = queryStatId;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for qqqTableId
+ *******************************************************************************/
+ public Integer getQqqTableId()
+ {
+ return (this.qqqTableId);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for qqqTableId
+ *******************************************************************************/
+ public void setQqqTableId(Integer qqqTableId)
+ {
+ this.qqqTableId = qqqTableId;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for qqqTableId
+ *******************************************************************************/
+ public QueryStatCriteriaField withQqqTableId(Integer qqqTableId)
+ {
+ this.qqqTableId = qqqTableId;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for name
+ *******************************************************************************/
+ public String getName()
+ {
+ return (this.name);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for name
+ *******************************************************************************/
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for name
+ *******************************************************************************/
+ public QueryStatCriteriaField withName(String name)
+ {
+ this.name = name;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for operator
+ *******************************************************************************/
+ public String getOperator()
+ {
+ return (this.operator);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for operator
+ *******************************************************************************/
+ public void setOperator(String operator)
+ {
+ this.operator = operator;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for operator
+ *******************************************************************************/
+ public QueryStatCriteriaField withOperator(String operator)
+ {
+ this.operator = operator;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for values
+ *******************************************************************************/
+ public String getValues()
+ {
+ return (this.values);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for values
+ *******************************************************************************/
+ public void setValues(String values)
+ {
+ this.values = values;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for values
+ *******************************************************************************/
+ public QueryStatCriteriaField withValues(String values)
+ {
+ this.values = values;
+ return (this);
+ }
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatJoinTable.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatJoinTable.java
new file mode 100644
index 00000000..47686a17
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatJoinTable.java
@@ -0,0 +1,194 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2023. 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 .
+ */
+
+package com.kingsrook.qqq.backend.core.model.querystats;
+
+
+import com.kingsrook.qqq.backend.core.model.data.QField;
+import com.kingsrook.qqq.backend.core.model.data.QRecord;
+import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
+import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
+import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
+
+
+/*******************************************************************************
+ ** QRecord Entity for QueryStatJoinTable table
+ *******************************************************************************/
+public class QueryStatJoinTable extends QRecordEntity
+{
+ public static final String TABLE_NAME = "queryStatJoinTable"; // todo - lowercase the first letter
+
+ @QField(isEditable = false)
+ private Integer id;
+
+ @QField(possibleValueSourceName = QueryStat.TABLE_NAME)
+ private Integer queryStatId;
+
+ @QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
+ private Integer qqqTableId;
+
+ @QField(maxLength = 10, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String type;
+
+
+
+ /*******************************************************************************
+ ** Default constructor
+ *******************************************************************************/
+ public QueryStatJoinTable()
+ {
+ }
+
+
+
+ /*******************************************************************************
+ ** Constructor that takes a QRecord
+ *******************************************************************************/
+ public QueryStatJoinTable(QRecord record)
+ {
+ populateFromQRecord(record);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for id
+ *******************************************************************************/
+ public Integer getId()
+ {
+ return (this.id);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for id
+ *******************************************************************************/
+ public void setId(Integer id)
+ {
+ this.id = id;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for id
+ *******************************************************************************/
+ public QueryStatJoinTable withId(Integer id)
+ {
+ this.id = id;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for queryStatId
+ *******************************************************************************/
+ public Integer getQueryStatId()
+ {
+ return (this.queryStatId);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for queryStatId
+ *******************************************************************************/
+ public void setQueryStatId(Integer queryStatId)
+ {
+ this.queryStatId = queryStatId;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for queryStatId
+ *******************************************************************************/
+ public QueryStatJoinTable withQueryStatId(Integer queryStatId)
+ {
+ this.queryStatId = queryStatId;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for qqqTableId
+ *******************************************************************************/
+ public Integer getQqqTableId()
+ {
+ return (this.qqqTableId);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for qqqTableId
+ *******************************************************************************/
+ public void setQqqTableId(Integer qqqTableId)
+ {
+ this.qqqTableId = qqqTableId;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for qqqTableId
+ *******************************************************************************/
+ public QueryStatJoinTable withQqqTableId(Integer qqqTableId)
+ {
+ this.qqqTableId = qqqTableId;
+ return (this);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for type
+ *******************************************************************************/
+ public String getType()
+ {
+ return (this.type);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for type
+ *******************************************************************************/
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for type
+ *******************************************************************************/
+ public QueryStatJoinTable withType(String type)
+ {
+ this.type = type;
+ return (this);
+ }
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatMetaDataProvider.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatMetaDataProvider.java
new file mode 100644
index 00000000..85d5f079
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatMetaDataProvider.java
@@ -0,0 +1,189 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2023. 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 .
+ */
+
+package com.kingsrook.qqq.backend.core.model.querystats;
+
+
+import java.util.List;
+import java.util.function.Consumer;
+import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ChildRecordListRenderer;
+import com.kingsrook.qqq.backend.core.exceptions.QException;
+import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
+import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
+import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
+import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
+import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
+import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
+import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
+import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
+import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
+import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
+import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
+import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
+import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
+import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
+import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
+import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
+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;
+
+
+/*******************************************************************************
+ **
+ *******************************************************************************/
+public class QueryStatMetaDataProvider
+{
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public void defineAll(QInstance instance, String backendName, Consumer backendDetailEnricher) throws QException
+ {
+ addJoins(instance);
+
+ defineQueryStatTable(instance, backendName, backendDetailEnricher);
+
+ instance.addTable(defineStandardTable(QueryStatJoinTable.TABLE_NAME, QueryStatJoinTable.class, backendName, backendDetailEnricher));
+
+ instance.addTable(defineStandardTable(QueryStatCriteriaField.TABLE_NAME, QueryStatCriteriaField.class, backendName, backendDetailEnricher)
+ .withExposedJoin(new ExposedJoin().withJoinTable(QueryStat.TABLE_NAME))
+ );
+
+ instance.addTable(defineStandardTable(QueryStatOrderByField.TABLE_NAME, QueryStatOrderByField.class, backendName, backendDetailEnricher));
+
+ instance.addPossibleValueSource(defineQueryStatPossibleValueSource());
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private void addJoins(QInstance instance)
+ {
+ instance.addJoin(new QJoinMetaData()
+ .withLeftTable(QueryStat.TABLE_NAME)
+ .withRightTable(QueryStatJoinTable.TABLE_NAME)
+ .withInferredName()
+ .withType(JoinType.ONE_TO_MANY)
+ .withJoinOn(new JoinOn("id", "queryStatId")));
+
+ instance.addJoin(new QJoinMetaData()
+ .withLeftTable(QueryStat.TABLE_NAME)
+ .withRightTable(QueryStatCriteriaField.TABLE_NAME)
+ .withInferredName()
+ .withType(JoinType.ONE_TO_MANY)
+ .withJoinOn(new JoinOn("id", "queryStatId")));
+
+ instance.addJoin(new QJoinMetaData()
+ .withLeftTable(QueryStat.TABLE_NAME)
+ .withRightTable(QueryStatOrderByField.TABLE_NAME)
+ .withInferredName()
+ .withType(JoinType.ONE_TO_MANY)
+ .withJoinOn(new JoinOn("id", "queryStatId")));
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private QTableMetaData defineQueryStatTable(QInstance instance, String backendName, Consumer backendDetailEnricher) throws QException
+ {
+ String joinTablesJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatJoinTable.TABLE_NAME);
+ String criteriaFieldsJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatCriteriaField.TABLE_NAME);
+ String orderByFieldsJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatOrderByField.TABLE_NAME);
+
+ QTableMetaData table = new QTableMetaData()
+ .withName(QueryStat.TABLE_NAME)
+ .withBackendName(backendName)
+ .withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
+ .withRecordLabelFormat("%s")
+ .withRecordLabelFields("id")
+ .withPrimaryKeyField("id")
+ .withFieldsFromEntity(QueryStat.class)
+ .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "action", "qqqTableId", "sessionId")))
+ .withSection(new QFieldSection("data", new QIcon().withName("dataset"), Tier.T2, List.of("queryText", "startTimestamp", "firstResultTimestamp", "firstResultMillis")))
+ .withSection(new QFieldSection("joins", new QIcon().withName("merge"), Tier.T2).withWidgetName(joinTablesJoinName + "Widget"))
+ .withSection(new QFieldSection("criteria", new QIcon().withName("filter_alt"), Tier.T2).withWidgetName(criteriaFieldsJoinName + "Widget"))
+ .withSection(new QFieldSection("orderBys", new QIcon().withName("sort_by_alpha"), Tier.T2).withWidgetName(orderByFieldsJoinName + "Widget"))
+ .withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
+
+ if(backendDetailEnricher != null)
+ {
+ backendDetailEnricher.accept(table);
+ }
+
+ instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(joinTablesJoinName)).withName(joinTablesJoinName + "Widget").withLabel("Join Tables").getWidgetMetaData());
+ instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(criteriaFieldsJoinName)).withName(criteriaFieldsJoinName + "Widget").withLabel("Criteria Fields").getWidgetMetaData());
+ instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(orderByFieldsJoinName)).withName(orderByFieldsJoinName + "Widget").withLabel("Order by Fields").getWidgetMetaData());
+
+ table.withAssociation(new Association().withName("queryStatJoinTables").withJoinName(joinTablesJoinName).withAssociatedTableName(QueryStatJoinTable.TABLE_NAME))
+ .withAssociation(new Association().withName("queryStatCriteriaFields").withJoinName(criteriaFieldsJoinName).withAssociatedTableName(QueryStatCriteriaField.TABLE_NAME))
+ .withAssociation(new Association().withName("queryStatOrderByFields").withJoinName(orderByFieldsJoinName).withAssociatedTableName(QueryStatOrderByField.TABLE_NAME));
+
+ table.getField("queryText").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("sql")));
+ table.getField("firstResultMillis").withDisplayFormat(DisplayFormat.COMMAS);
+
+ instance.addTable(table);
+ return (table);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private QTableMetaData defineStandardTable(String tableName, Class extends QRecordEntity> entityClass, String backendName, Consumer backendDetailEnricher) throws QException
+ {
+ QTableMetaData table = new QTableMetaData()
+ .withName(tableName)
+ .withBackendName(backendName)
+ .withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
+ .withRecordLabelFormat("%d")
+ .withRecordLabelFields("id")
+ .withPrimaryKeyField("id")
+ .withFieldsFromEntity(entityClass)
+ .withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
+
+ if(backendDetailEnricher != null)
+ {
+ backendDetailEnricher.accept(table);
+ }
+
+ return (table);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public QPossibleValueSource defineQueryStatPossibleValueSource()
+ {
+ return (new QPossibleValueSource()
+ .withType(QPossibleValueSourceType.TABLE)
+ .withName(QueryStat.TABLE_NAME)
+ .withTableName(QueryStat.TABLE_NAME));
+ }
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStatFilterCriteria.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatOrderByField.java
similarity index 61%
rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStatFilterCriteria.java
rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatOrderByField.java
index ffb29a8b..353221c1 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStatFilterCriteria.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/querystats/QueryStatOrderByField.java
@@ -19,21 +19,84 @@
* along with this program. If not, see .
*/
-package com.kingsrook.qqq.backend.core.actions.tables.helpers.querystats;
+package com.kingsrook.qqq.backend.core.model.querystats;
+import com.kingsrook.qqq.backend.core.model.data.QField;
+import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
+import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
+import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
/*******************************************************************************
- **
+ ** QRecord Entity for QueryStatOrderByField table
*******************************************************************************/
-public class QueryStatFilterCriteria extends QRecordEntity
+public class QueryStatOrderByField extends QRecordEntity
{
+ public static final String TABLE_NAME = "queryStatOrderByField";
+
+ @QField(isEditable = false)
+ private Integer id;
+
+ @QField(possibleValueSourceName = QueryStat.TABLE_NAME)
private Integer queryStatId;
- private String fieldName;
- private String operator;
- private String values;
+
+ @QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
+ private Integer qqqTableId;
+
+ @QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
+ private String name;
+
+
+
+ /*******************************************************************************
+ ** Default constructor
+ *******************************************************************************/
+ public QueryStatOrderByField()
+ {
+ }
+
+
+
+ /*******************************************************************************
+ ** Constructor that takes a QRecord
+ *******************************************************************************/
+ public QueryStatOrderByField(QRecord record)
+ {
+ populateFromQRecord(record);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for id
+ *******************************************************************************/
+ public Integer getId()
+ {
+ return (this.id);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for id
+ *******************************************************************************/
+ public void setId(Integer id)
+ {
+ this.id = id;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for id
+ *******************************************************************************/
+ public QueryStatOrderByField withId(Integer id)
+ {
+ this.id = id;
+ return (this);
+ }
@@ -60,7 +123,7 @@ public class QueryStatFilterCriteria extends QRecordEntity
/*******************************************************************************
** Fluent setter for queryStatId
*******************************************************************************/
- public QueryStatFilterCriteria withQueryStatId(Integer queryStatId)
+ public QueryStatOrderByField withQueryStatId(Integer queryStatId)
{
this.queryStatId = queryStatId;
return (this);
@@ -69,93 +132,62 @@ public class QueryStatFilterCriteria extends QRecordEntity
/*******************************************************************************
- ** Getter for fieldName
+ ** Getter for qqqTableId
*******************************************************************************/
- public String getFieldName()
+ public Integer getQqqTableId()
{
- return (this.fieldName);
+ return (this.qqqTableId);
}
/*******************************************************************************
- ** Setter for fieldName
+ ** Setter for qqqTableId
*******************************************************************************/
- public void setFieldName(String fieldName)
+ public void setQqqTableId(Integer qqqTableId)
{
- this.fieldName = fieldName;
+ this.qqqTableId = qqqTableId;
}
/*******************************************************************************
- ** Fluent setter for fieldName
+ ** Fluent setter for qqqTableId
*******************************************************************************/
- public QueryStatFilterCriteria withFieldName(String fieldName)
+ public QueryStatOrderByField withQqqTableId(Integer qqqTableId)
{
- this.fieldName = fieldName;
+ this.qqqTableId = qqqTableId;
return (this);
}
/*******************************************************************************
- ** Getter for operator
+ ** Getter for name
*******************************************************************************/
- public String getOperator()
+ public String getName()
{
- return (this.operator);
+ return (this.name);
}
/*******************************************************************************
- ** Setter for operator
+ ** Setter for name
*******************************************************************************/
- public void setOperator(String operator)
+ public void setName(String name)
{
- this.operator = operator;
+ this.name = name;
}
/*******************************************************************************
- ** Fluent setter for operator
+ ** Fluent setter for name
*******************************************************************************/
- public QueryStatFilterCriteria withOperator(String operator)
+ public QueryStatOrderByField withName(String name)
{
- this.operator = operator;
- return (this);
- }
-
-
-
- /*******************************************************************************
- ** Getter for values
- *******************************************************************************/
- public String getValues()
- {
- return (this.values);
- }
-
-
-
- /*******************************************************************************
- ** Setter for values
- *******************************************************************************/
- public void setValues(String values)
- {
- this.values = values;
- }
-
-
-
- /*******************************************************************************
- ** Fluent setter for values
- *******************************************************************************/
- public QueryStatFilterCriteria withValues(String values)
- {
- this.values = values;
+ this.name = name;
return (this);
}