diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/QueryValidator.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/QueryValidator.java index bc7b00f242..a6ad1583fe 100644 --- a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/QueryValidator.java +++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/QueryValidator.java @@ -19,8 +19,6 @@ import com.yahoo.elide.datastores.aggregation.query.ColumnProjection; import com.yahoo.elide.datastores.aggregation.query.Query; -import org.apache.commons.collections.CollectionUtils; - import java.util.List; import java.util.Map; import java.util.Set; @@ -54,16 +52,6 @@ public QueryValidator(Query query, Set allFields, EntityDictionary dicti public void validate() { validateHavingClause(query.getHavingFilter()); validateSorting(); - validateMetricFunction(); - } - - /** - * Checks to make sure at least one metric is being aggregated on. - */ - private void validateMetricFunction() { - if (CollectionUtils.isEmpty(metrics)) { - throw new InvalidOperationException("Must provide at least one metric in query"); - } } /** @@ -145,6 +133,12 @@ private void validateSortingPath(Path path, Set allFields) { throw new UnsupportedOperationException( "Currently sorting on double nested fields is not supported"); } + + if (metrics.isEmpty() && pathElements.size() > 1) { + throw new UnsupportedOperationException( + "Query with no metric can't sort on nested field."); + } + Path.PathElement currentElement = pathElements.get(0); String currentField = currentElement.getFieldName(); Class currentClass = currentElement.getType(); diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryConstructor.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryConstructor.java index d422bc93b4..e35ae1184c 100644 --- a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryConstructor.java +++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryConstructor.java @@ -90,7 +90,9 @@ public SQLQuery resolveTemplate(Query clientQuery, Set groupByDimensions = template.getGroupByDimensions(); if (!groupByDimensions.isEmpty()) { - builder.groupByClause(constructGroupByWithReference(groupByDimensions, queriedTable)); + if (!clientQuery.getMetrics().isEmpty()) { + builder.groupByClause(constructGroupByWithReference(groupByDimensions, queriedTable)); + } joinPaths.addAll(extractJoinPaths(groupByDimensions, queriedTable)); } @@ -200,6 +202,10 @@ private String constructProjectionWithReference(SQLQueryTemplate template, SQLAn }) .collect(Collectors.toList()); + if (metricProjections.isEmpty()) { + return "DISTINCT " + String.join(",", dimensionProjections); + } + return Stream.concat(metricProjections.stream(), dimensionProjections.stream()) .collect(Collectors.joining(",")); } diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryEngine.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryEngine.java index cbeb3a5f58..b4404131a8 100644 --- a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryEngine.java +++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/queryengines/sql/SQLQueryEngine.java @@ -16,6 +16,7 @@ import com.yahoo.elide.core.pagination.Pagination; import com.yahoo.elide.datastores.aggregation.QueryEngine; import com.yahoo.elide.datastores.aggregation.metadata.MetaDataStore; +import com.yahoo.elide.datastores.aggregation.metadata.metric.MetricFunctionInvocation; import com.yahoo.elide.datastores.aggregation.metadata.models.AnalyticView; import com.yahoo.elide.datastores.aggregation.metadata.models.MetricFunction; import com.yahoo.elide.datastores.aggregation.metadata.models.Table; @@ -37,6 +38,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -169,7 +171,22 @@ protected SQLQuery toSQL(Query query) { timeDimension); }) .reduce(SQLQueryTemplate::merge) - .orElseThrow(() -> new InvalidPredicateException("Metric function not found")); + .orElse(new SQLQueryTemplate() { + @Override + public List getMetrics() { + return Collections.emptyList(); + } + + @Override + public Set getNonTimeDimensions() { + return groupByDimensions; + } + + @Override + public TimeDimensionProjection getTimeDimension() { + return timeDimension; + } + }); return new SQLQueryConstructor(metadataDictionary).resolveTemplate( query, diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/QueryValidatorTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/QueryValidatorTest.java index 2a616199f3..e0b7f2ba45 100644 --- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/QueryValidatorTest.java +++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/QueryValidatorTest.java @@ -36,15 +36,19 @@ public static void init() { @Test public void testNoMetricQuery() { + Map sortMap = new TreeMap<>(); + sortMap.put("country.name", Sorting.SortOrder.asc); + Query query = Query.builder() .analyticView(playerStatsTable) .groupByDimension(toProjection(playerStatsTable.getDimension("overallRating"))) + .sorting(new Sorting(sortMap)) .build(); QueryValidator validator = new QueryValidator(query, Collections.singleton("overallRating"), dictionary); - InvalidOperationException exception = assertThrows(InvalidOperationException.class, validator::validate); - assertEquals("Invalid operation: 'Must provide at least one metric in query'", exception.getMessage()); + UnsupportedOperationException exception = assertThrows(UnsupportedOperationException.class, validator::validate); + assertEquals("Query with no metric can't sort on nested field.", exception.getMessage()); } @Test diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/integration/AggregationDataStoreIntegrationTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/integration/AggregationDataStoreIntegrationTest.java index 3ba415ff43..b4de838de7 100644 --- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/integration/AggregationDataStoreIntegrationTest.java +++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/integration/AggregationDataStoreIntegrationTest.java @@ -116,6 +116,60 @@ public void basicAggregationTest() throws Exception { runQueryWithExpectedResult(graphQLRequest, expected); } + @Test + public void noMetricQueryTest() throws Exception { + String graphQLRequest = document( + selection( + field( + "playerStatsWithView", + arguments( + argument("sort", "\"countryViewRelationshipIsoCode\"") + ), + selections( + field( + "country", + selections( + field("name"), + field("isoCode") + ) + ), + field("countryViewRelationshipIsoCode") + ) + ) + ) + ).toQuery(); + + String expected = document( + selections( + field( + "playerStatsWithView", + selections( + field( + "country", + selections( + field("name", "Hong Kong"), + field("isoCode", "HKG") + ) + ), + field("countryViewRelationshipIsoCode", "HKG") + ), + selections( + field( + "country", + selections( + field("name", "United States"), + field("isoCode", "USA") + ) + ), + field("countryViewRelationshipIsoCode", "USA") + ) + ) + ) + ).toResponse(); + + runQueryWithExpectedResult(graphQLRequest, expected); + } + @Test public void whereFilterTest() throws Exception { String graphQLRequest = document( @@ -609,29 +663,6 @@ public void sortingOnMetricNotInQueryTest() throws Exception { runQueryWithExpectedError(graphQLRequest, expected); } - @Test - public void noMetricQueryTest() throws Exception { - String graphQLRequest = document( - selection( - field( - "playerStats", - selections( - field( - "country", - selections( - field("name") - ) - ) - ) - ) - ) - ).toQuery(); - - String expected = "\"Exception while fetching data (/playerStats) : Invalid operation: 'Must provide at least one metric in query'\""; - - runQueryWithExpectedError(graphQLRequest, expected); - } - @Test public void sortingMultipleLevelNesting() throws Exception { String graphQLRequest = document(