diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryCountAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryCountAction.java index cf2c35cc..a79b3095 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryCountAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryCountAction.java @@ -26,7 +26,6 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput; import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput; -import com.kingsrook.qqq.backend.core.utils.CollectionUtils; /******************************************************************************* @@ -43,14 +42,7 @@ public class MemoryCountAction implements CountInterface { try { - if(CollectionUtils.nullSafeHasContents(countInput.getQueryJoins())) - { - throw (new UnsupportedOperationException("Performing counts on tables with exposed joins is currently not supported by the Memory Backend.")); - } - - CountOutput countOutput = new CountOutput(); - countOutput.setCount(MemoryRecordStore.getInstance().count(countInput)); - countOutput.setDistinctCount(countOutput.getCount()); + CountOutput countOutput = MemoryRecordStore.getInstance().count(countInput); return (countOutput); } catch(Exception e) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryRecordStore.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryRecordStore.java index af8f49ca..b549c1b1 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryRecordStore.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryRecordStore.java @@ -33,10 +33,12 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.DateTimeGroupBy; @@ -54,6 +56,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext; @@ -77,6 +80,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.ListingHash; import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils; +import org.apache.commons.lang3.BooleanUtils; /******************************************************************************* @@ -335,17 +339,55 @@ public class MemoryRecordStore /******************************************************************************* ** *******************************************************************************/ - public Integer count(CountInput input) throws QException + public CountOutput count(CountInput input) throws QException { + //////////////////////////////////////////////////////////////////////////////////////////// + // set up a query input - we'll implement count by counting the records in a query output // + //////////////////////////////////////////////////////////////////////////////////////////// QueryInput queryInput = new QueryInput(); queryInput.setTableName(input.getTableName()); + if(input.getFilter() != null) { queryInput.setFilter(input.getFilter().clone().withSkip(null).withLimit(null)); } + + if(input.getQueryJoins() != null) + { + queryInput.setQueryJoins(new ArrayList<>()); + for(QueryJoin queryJoin : input.getQueryJoins()) + { + queryInput.getQueryJoins().add(queryJoin.clone()); + } + } + + /////////////////// + // run the query // + /////////////////// List queryResult = query(queryInput); - return (queryResult.size()); + //////////////////////// + // build count output // + //////////////////////// + CountOutput countOutput = new CountOutput(); + countOutput.setCount(queryResult.size()); + + ////////////////////////////////////// + // figure out distinct if requested // + ////////////////////////////////////// + if(BooleanUtils.isTrue(input.getIncludeDistinctCount())) + { + QTableMetaData table = QContext.getQInstance().getTable(input.getTableName()); + String primaryKeyField = table.getPrimaryKeyField(); + Set distinctValues = new HashSet<>(); + for(QRecord record : queryResult) + { + distinctValues.add(record.getValue(primaryKeyField)); + } + countOutput.setDistinctCount(distinctValues.size()); + } + + return (countOutput); } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java index 80e17172..d9783435 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/memory/MemoryBackendModuleTest.java @@ -47,6 +47,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput; import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; @@ -56,6 +57,7 @@ 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.actions.tables.query.QueryInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput; @@ -725,6 +727,38 @@ class MemoryBackendModuleTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testJoins() throws QException + { + QContext.getQSession().setSecurityKeyValues(Map.of(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, List.of(true))); + + new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_ORDER).withRecords(List.of( + new QRecord().withValue("id", 1), + new QRecord().withValue("id", 2) + ))); + + new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_LINE_ITEM).withRecords(List.of( + new QRecord().withValue("sku", "A").withValue("orderId", 1), + new QRecord().withValue("sku", "B").withValue("orderId", 1), + new QRecord().withValue("sku", "A").withValue("orderId", 2) + ))); + + QueryOutput queryOutput = new QueryAction().execute(new QueryInput(TestUtils.TABLE_NAME_ORDER) + .withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_LINE_ITEM))); + assertEquals(3, queryOutput.getRecords().size()); + + CountOutput countOutput = new CountAction().execute(new CountInput(TestUtils.TABLE_NAME_ORDER) + .withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_LINE_ITEM)) + .withIncludeDistinctCount(true)); + assertEquals(3, countOutput.getCount()); + assertEquals(2, countOutput.getDistinctCount()); + } + + + /******************************************************************************* ** *******************************************************************************/