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 index 59ee1db6..42a77120 100644 --- 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 @@ -40,6 +40,7 @@ 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.QFilterOrderBy; 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; @@ -47,6 +48,7 @@ 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.querystats.QueryStatOrderByField; 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; @@ -286,12 +288,16 @@ public class QueryStatManager if(queryStat.getQueryFilter() != null && queryStat.getQueryFilter().hasAnyCriteria()) { List queryStatCriteriaFieldList = new ArrayList<>(); - QQueryFilter queryFilter = queryStat.getQueryFilter(); - processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, queryFilter); + processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, queryStat.getQueryFilter()); queryStat.setQueryStatCriteriaFieldList(queryStatCriteriaFieldList); } - // todo - orderbys + if(CollectionUtils.nullSafeHasContents(queryStat.getQueryFilter().getOrderBys())) + { + List queryStatOrderByFieldList = new ArrayList<>(); + processOrderByFromFilter(qqqTableId, queryStatOrderByFieldList, queryStat.getQueryFilter()); + queryStat.setQueryStatOrderByFieldList(queryStatOrderByFieldList); + } queryStatQRecordsToInsert.add(queryStat.toQRecord()); } @@ -370,6 +376,37 @@ public class QueryStatManager + /******************************************************************************* + ** + *******************************************************************************/ + private static void processOrderByFromFilter(Integer qqqTableId, List queryStatOrderByFieldList, QQueryFilter queryFilter) throws QException + { + for(QFilterOrderBy orderBy : CollectionUtils.nonNullList(queryFilter.getOrderBys())) + { + String fieldName = orderBy.getFieldName(); + QueryStatOrderByField queryStatOrderByField = new QueryStatOrderByField(); + + if(fieldName.contains(".")) + { + String[] parts = fieldName.split("\\."); + if(parts.length > 1) + { + queryStatOrderByField.setQqqTableId(getQQQTableId(parts[0])); + queryStatOrderByField.setName(parts[1]); + } + } + else + { + queryStatOrderByField.setQqqTableId(qqqTableId); + queryStatOrderByField.setName(fieldName); + } + + queryStatOrderByFieldList.add(queryStatOrderByField); + } + } + + + /******************************************************************************* ** *******************************************************************************/ 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/actions/tables/helpers/querystats/QueryStat.java deleted file mode 100644 index 2c35f9f4..00000000 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStat.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * 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.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 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.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; - - -/******************************************************************************* - ** - *******************************************************************************/ -public class QueryStat extends QRecordEntity -{ - public static final String TABLE_NAME = "queryStat"; - - @QField() - private Integer id; - - @QField() - private String tableName; - - @QField() - private Instant startTimestamp; - - @QField() - private Instant firstResultTimestamp; - - @QField() - private Integer firstResultMillis; - - @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE) - private String joinTables; - - @QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE) - private String orderBys; - - @QAssociation(name = "queryStatFilterCriteria") - private List queryStatFilterCriteriaList; - - - - /******************************************************************************* - ** - *******************************************************************************/ - public void setJoinTables(Collection joinTableNames) - { - if(CollectionUtils.nullSafeIsEmpty(joinTableNames)) - { - setJoinTables((String) null); - } - - setJoinTables(joinTableNames.stream().sorted().collect(Collectors.joining(","))); - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - public void setQQueryFilter(QQueryFilter filter) - { - 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(","))); - } - } - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - private static void processFilterCriteria(QQueryFilter filter, ArrayList criteriaList) - { - 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); - } - } - - - - /******************************************************************************* - ** 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 startTimestamp - *******************************************************************************/ - public Instant getStartTimestamp() - { - return (this.startTimestamp); - } - - - - /******************************************************************************* - ** Setter for startTimestamp - *******************************************************************************/ - public void setStartTimestamp(Instant startTimestamp) - { - this.startTimestamp = startTimestamp; - } - - - - /******************************************************************************* - ** Fluent setter for startTimestamp - *******************************************************************************/ - public QueryStat withStartTimestamp(Instant startTimestamp) - { - this.startTimestamp = startTimestamp; - return (this); - } - - - - /******************************************************************************* - ** Getter for firstResultTimestamp - *******************************************************************************/ - public Instant getFirstResultTimestamp() - { - return (this.firstResultTimestamp); - } - - - - /******************************************************************************* - ** Setter for firstResultTimestamp - *******************************************************************************/ - public void setFirstResultTimestamp(Instant firstResultTimestamp) - { - this.firstResultTimestamp = firstResultTimestamp; - } - - - - /******************************************************************************* - ** Fluent setter for firstResultTimestamp - *******************************************************************************/ - public QueryStat withFirstResultTimestamp(Instant firstResultTimestamp) - { - this.firstResultTimestamp = firstResultTimestamp; - return (this); - } - - - - /******************************************************************************* - ** Getter for firstResultMillis - *******************************************************************************/ - public Integer getFirstResultMillis() - { - return (this.firstResultMillis); - } - - - - /******************************************************************************* - ** Setter for firstResultMillis - *******************************************************************************/ - public void setFirstResultMillis(Integer firstResultMillis) - { - this.firstResultMillis = firstResultMillis; - } - - - - /******************************************************************************* - ** Fluent setter for firstResultMillis - *******************************************************************************/ - public QueryStat withFirstResultMillis(Integer firstResultMillis) - { - this.firstResultMillis = firstResultMillis; - return (this); - } - - - - /******************************************************************************* - ** Getter for joinTables - *******************************************************************************/ - public String getJoinTables() - { - return (this.joinTables); - } - - - - /******************************************************************************* - ** Setter for joinTables - *******************************************************************************/ - public void setJoinTables(String joinTables) - { - this.joinTables = joinTables; - } - - - - /******************************************************************************* - ** Fluent setter for joinTables - *******************************************************************************/ - public QueryStat withJoinTables(String joinTables) - { - this.joinTables = joinTables; - return (this); - } - - - - /******************************************************************************* - ** Getter for queryStatFilterCriteriaList - *******************************************************************************/ - public List getQueryStatFilterCriteriaList() - { - return (this.queryStatFilterCriteriaList); - } - - - - /******************************************************************************* - ** Setter for queryStatFilterCriteriaList - *******************************************************************************/ - public void setQueryStatFilterCriteriaList(List queryStatFilterCriteriaList) - { - this.queryStatFilterCriteriaList = queryStatFilterCriteriaList; - } - - - - /******************************************************************************* - ** Fluent setter for queryStatFilterCriteriaList - *******************************************************************************/ - public QueryStat withQueryStatFilterCriteriaList(List queryStatFilterCriteriaList) - { - this.queryStatFilterCriteriaList = queryStatFilterCriteriaList; - return (this); - } - - - - /******************************************************************************* - ** Getter for orderBys - *******************************************************************************/ - public String getOrderBys() - { - return (this.orderBys); - } - - - - /******************************************************************************* - ** Setter for orderBys - *******************************************************************************/ - public void setOrderBys(String orderBys) - { - this.orderBys = orderBys; - } - - - - /******************************************************************************* - ** Fluent setter for orderBys - *******************************************************************************/ - public QueryStat withOrderBys(String orderBys) - { - this.orderBys = orderBys; - return (this); - } - - - - /******************************************************************************* - ** 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 QueryStat withId(Integer id) - { - this.id = id; - return (this); - } - -} 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/actions/tables/helpers/querystats/QueryStatFilterCriteria.java deleted file mode 100644 index ffb29a8b..00000000 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStatFilterCriteria.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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.querystats; - - -import com.kingsrook.qqq.backend.core.model.data.QRecordEntity; - - -/******************************************************************************* - ** - *******************************************************************************/ -public class QueryStatFilterCriteria extends QRecordEntity -{ - private Integer queryStatId; - private String fieldName; - private String operator; - private String values; - - - - /******************************************************************************* - ** 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 QueryStatFilterCriteria withQueryStatId(Integer queryStatId) - { - this.queryStatId = queryStatId; - return (this); - } - - - - /******************************************************************************* - ** Getter for fieldName - *******************************************************************************/ - public String getFieldName() - { - return (this.fieldName); - } - - - - /******************************************************************************* - ** Setter for fieldName - *******************************************************************************/ - public void setFieldName(String fieldName) - { - this.fieldName = fieldName; - } - - - - /******************************************************************************* - ** Fluent setter for fieldName - *******************************************************************************/ - public QueryStatFilterCriteria withFieldName(String fieldName) - { - this.fieldName = fieldName; - 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 QueryStatFilterCriteria 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 QueryStatFilterCriteria withValues(String values) - { - this.values = values; - return (this); - } - -} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStatManager.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStatManager.java deleted file mode 100644 index eda40a98..00000000 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/helpers/querystats/QueryStatManager.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 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.querystats; - - -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.InsertAction; -import com.kingsrook.qqq.backend.core.context.QContext; -import com.kingsrook.qqq.backend.core.logging.QLogger; -import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; -import com.kingsrook.qqq.backend.core.model.metadata.QInstance; -import com.kingsrook.qqq.backend.core.model.session.QSession; -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(Supplier sessionSupplier) - { - qInstance = QContext.getQInstance(); - this.sessionSupplier = sessionSupplier; - - active = true; - queryStats = new ArrayList<>(); - - executorService = Executors.newSingleThreadScheduledExecutor(); - executorService.scheduleAtFixedRate(new QueryStatManagerInsertJob(), 60, 60, TimeUnit.SECONDS); - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - public void stop() - { - active = false; - queryStats.clear(); - - if(executorService != null) - { - executorService.shutdown(); - executorService = null; - } - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - public void add(QueryStat queryStat) - { - if(active) - { - synchronized(this) - { - if(queryStat.getFirstResultTimestamp() == null) - { - //////////////////////////////////////////////// - // in case it didn't get set in the interface // - //////////////////////////////////////////////// - queryStat.setFirstResultTimestamp(Instant.now()); - } - - /////////////////////////////////////////////// - // 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); - } - - queryStats.add(queryStat); - } - } - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - private List getListAndReset() - { - if(queryStats.isEmpty()) - { - return Collections.emptyList(); - } - - synchronized(this) - { - List returnList = queryStats; - queryStats = new ArrayList<>(); - return (returnList); - } - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - 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; - } - - try - { - InsertInput insertInput = new InsertInput(); - insertInput.setTableName(QueryStat.TABLE_NAME); - insertInput.setRecords(list.stream().map(qs -> qs.toQRecord()).toList()); - new InsertAction().execute(insertInput); - } - catch(Exception e) - { - LOG.error("Error inserting query stats", e); - } - } - finally - { - QContext.clear(); - } - } - } - -} diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java index 87d0032d..67c337ab 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QueryActionTest.java @@ -28,13 +28,21 @@ import java.util.List; import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop; import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe; +import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager; import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; 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.Capability; +import com.kingsrook.qqq.backend.core.model.querystats.QueryStat; +import com.kingsrook.qqq.backend.core.model.querystats.QueryStatMetaDataProvider; +import com.kingsrook.qqq.backend.core.model.session.QSession; +import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider; import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockQueryAction; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils; @@ -390,6 +398,59 @@ class QueryActionTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testQueryManager() throws QException + { + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // add tables for QueryStats, and turn them on in the memory backend, then start the query-stat manager // + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + QInstance qInstance = QContext.getQInstance(); + qInstance.getBackend(TestUtils.MEMORY_BACKEND_NAME).withCapability(Capability.QUERY_STATS); + new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null); + new QueryStatMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null); + QueryStatManager.getInstance().start(QContext.getQInstance(), QSession::new); + + ///////////////////////////////////////////////////////////////////////////////// + // insert some order "trees", then query them, so some stats will get recorded // + ///////////////////////////////////////////////////////////////////////////////// + insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations(); + QueryInput queryInput = new QueryInput(); + queryInput.setTableName(TestUtils.TABLE_NAME_ORDER); + queryInput.setIncludeAssociations(true); + queryInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderBy("id"))); + QContext.pushAction(queryInput); + QueryOutput queryOutput = new QueryAction().execute(queryInput); + + //////////////////////////////////////////////////////////// + // run the stat manager (so we don't have to wait for it) // + //////////////////////////////////////////////////////////// + QueryStatManager.getInstance().storeStatsNow(); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // stat manager expects to be ran in a thread, where it needs to clear context, so reset context after it // + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + QContext.init(qInstance, new QSession()); + + //////////////////////////////////////////////// + // query to see that some stats were inserted // + //////////////////////////////////////////////// + queryInput = new QueryInput(); + queryInput.setTableName(QueryStat.TABLE_NAME); + QContext.pushAction(queryInput); + queryOutput = new QueryAction().execute(queryInput); + + /////////////////////////////////////////////////////////////////////////////////// + // selecting all of those associations should have caused (at least?) 4 queries. // + // this is the most basic test here, but we'll take it. // + /////////////////////////////////////////////////////////////////////////////////// + assertThat(queryOutput.getRecords().size()).isGreaterThanOrEqualTo(4); + } + + + /******************************************************************************* ** *******************************************************************************/