[CALCITE-2689] In ElasticSearch adapter, allow grouping on non-textual fields like date and number
Consider field type when populating `missing` (value) in Elasticsearch terms aggregations. Close apache/calcite#946 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/ed3da62d Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/ed3da62d Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/ed3da62d Branch: refs/heads/master Commit: ed3da62d75ead6c00bb73470c3436e51f6f77197 Parents: 08aefb0 Author: Andrei Sereda <[email protected]> Authored: Tue Nov 20 00:10:47 2018 -0500 Committer: Julian Hyde <[email protected]> Committed: Fri Nov 30 17:55:09 2018 -0800 ---------------------------------------------------------------------- elasticsearch/pom.xml | 5 + .../elasticsearch/ElasticsearchJson.java | 67 +++++-- .../elasticsearch/ElasticsearchMapping.java | 188 +++++++++++++++++++ .../elasticsearch/ElasticsearchTable.java | 13 +- .../elasticsearch/ElasticsearchTransport.java | 17 ++ .../adapter/elasticsearch/Scrolling.java | 1 - .../adapter/elasticsearch/AggregationTest.java | 77 ++++++-- .../adapter/elasticsearch/BooleanLogicTest.java | 7 +- .../adapter/elasticsearch/ScrollingTest.java | 1 - 9 files changed, 338 insertions(+), 38 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/pom.xml ---------------------------------------------------------------------- diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index ec9a0e0..86248ba 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -68,6 +68,11 @@ limitations under the License. <artifactId>jackson-annotations</artifactId> </dependency> <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + <optional>true</optional> + </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchJson.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchJson.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchJson.java index 8a6b011..e389ecf 100644 --- a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchJson.java +++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchJson.java @@ -26,14 +26,15 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.time.Duration; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Deque; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -50,7 +51,7 @@ import java.util.stream.StreamSupport; import static java.util.Collections.unmodifiableMap; /** - * Internal objects (and deserializers) used to parse elastic search results + * Internal objects (and deserializers) used to parse Elasticsearch results * (which are in JSON format). * * <p>Since we're using basic row-level rest client http response has to be @@ -58,13 +59,6 @@ import static java.util.Collections.unmodifiableMap; */ final class ElasticsearchJson { - /** - * Used as special aggregation key for missing values (documents which are missing a field). - * Buckets with that value are then converted to {@code null}s in flat tabular format. - * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-sum-aggregation.html">Missing Value</a> - */ - static final JsonNode MISSING_VALUE = JsonNodeFactory.instance.textNode("__MISSING__"); - private ElasticsearchJson() {} /** @@ -87,6 +81,50 @@ final class ElasticsearchJson { } /** + * Visits Elasticsearch + * <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html">mapping + * properties</a> and calls consumer for each {@code field / type} pair. + * Nested fields are represented as {@code foo.bar.qux}. + */ + static void visitMappingProperties(ObjectNode mapping, + BiConsumer<String, String> consumer) { + Objects.requireNonNull(mapping, "mapping"); + Objects.requireNonNull(consumer, "consumer"); + visitMappingProperties(new ArrayDeque<>(), mapping, consumer); + } + + private static void visitMappingProperties(Deque<String> path, + ObjectNode mapping, BiConsumer<String, String> consumer) { + Objects.requireNonNull(mapping, "mapping"); + if (mapping.isMissingNode()) { + return; + } + + if (mapping.has("properties")) { + // recurse + visitMappingProperties(path, (ObjectNode) mapping.get("properties"), consumer); + return; + } + + if (mapping.has("type")) { + // this is leaf (register field / type mapping) + consumer.accept(String.join(".", path), mapping.get("type").asText()); + return; + } + + // otherwise continue visiting mapping(s) + Iterable<Map.Entry<String, JsonNode>> iter = mapping::fields; + for (Map.Entry<String, JsonNode> entry : iter) { + final String name = entry.getKey(); + final ObjectNode node = (ObjectNode) entry.getValue(); + path.add(name); + visitMappingProperties(path, node, consumer); + path.removeLast(); + } + } + + + /** * Identifies a calcite row (as in relational algebra) */ private static class RowKey { @@ -601,19 +639,24 @@ final class ElasticsearchJson { * Determines if current key is a missing field key. Missing key is returned when document * does not have pivoting attribute (example {@code GROUP BY _MAP['a.b.missing']}). It helps * grouping documents which don't have a field. In relational algebra this - * would be {@code null}. + * would normally be {@code null}. + * + * <p>Please note that missing value is different for each type. * * @param key current {@code key} (usually string) as returned by ES * @return {@code true} if this value - * @see #MISSING_VALUE */ private static boolean isMissingBucket(JsonNode key) { - return MISSING_VALUE.equals(key); + return ElasticsearchMapping.Datatype.isMissingValue(key); } private static Bucket parseBucket(JsonParser parser, String name, ObjectNode node) throws JsonProcessingException { + if (!node.has("key")) { + throw new IllegalArgumentException("No 'key' attribute for " + node); + } + final JsonNode keyNode = node.get("key"); final Object key; if (isMissingBucket(keyNode) || keyNode.isNull()) { http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java new file mode 100644 index 0000000..93a8049 --- /dev/null +++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMapping.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.adapter.elasticsearch; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.google.common.collect.ImmutableMap; + +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; + +/** + * Stores Elasticsearch + * <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html"> + * mapping</a> information for particular index/type. This information is + * extracted from {@code /$index/$type/_mapping} endpoint. + * + * <p>Instances of this class are immutable. + */ +class ElasticsearchMapping { + + private final String index; + + private final String type; + + private final Map<String, Datatype> mapping; + + ElasticsearchMapping(final String index, final String type, + final Map<String, String> mapping) { + this.index = Objects.requireNonNull(index, "index"); + this.type = Objects.requireNonNull(type, "type"); + Objects.requireNonNull(mapping, "mapping"); + + final Map<String, Datatype> transformed = mapping.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> new Datatype(e.getValue()))); + this.mapping = ImmutableMap.copyOf(transformed); + } + + /** + * Returns ES schema for each field. Mapping is represented as field name + * {@code foo.bar.qux} and type ({@code keyword}, {@code boolean}, + * {@code long}). + * + * @return immutable mapping between field and ES type + * + * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html">Mapping Types</a> + */ + Map<String, Datatype> mapping() { + return this.mapping; + } + + /** + * Used as special aggregation key for missing values (documents that are + * missing a field). + * + * <p>Buckets with that value are then converted to {@code null}s in flat + * tabular format. + * + * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-sum-aggregation.html">Missing Value</a> + */ + Optional<JsonNode> missingValueFor(String fieldName) { + if (!mapping().containsKey(fieldName)) { + final String message = String.format(Locale.ROOT, + "Field %s not defined for %s/%s", fieldName, index, type); + throw new IllegalArgumentException(message); + } + + return mapping().get(fieldName).missingValue(); + } + + String index() { + return this.index; + } + + String type() { + return this.type; + } + + /** + * Represents elastic data-type, like {@code long}, {@code keyword}, + * {@code date} etc. + * + * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html">Mapping Types</a> + */ + static class Datatype { + private static final JsonNodeFactory FACTORY = JsonNodeFactory.instance; + + // pre-cache missing values + private static final Set<JsonNode> MISSING_VALUES = + Stream.of("string", // for ES2 + "text", "keyword", + "date", "long", "integer", "double", "float") + .map(Datatype::missingValueForType) + .collect(Collectors.toSet()); + + private final String name; + private final JsonNode missingValue; + + private Datatype(final String name) { + this.name = Objects.requireNonNull(name, "name"); + this.missingValue = missingValueForType(name); + } + + /** + * Mapping between ES type and json value that represents + * {@code missing value} during aggregations. This value can't be + * {@code null} and should match type or the field (for ES long type it + * also has to be json integer, for date it has to match date format or be + * integer (millis epoch) etc. + * + * <p>It is used for terms aggregations to represent SQL {@code null}. + * + * @param name name of the type ({@code long}, {@code keyword} ...) + * + * @return json that will be used in elastic search terms aggregation for + * missing value + * + * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html#_missing_value_13">Missing Value</a> + */ + private static @Nullable JsonNode missingValueForType(String name) { + switch (name) { + case "string": // for ES2 + case "text": + case "keyword": + return FACTORY.textNode("__MISSING__"); + case "long": + return FACTORY.numberNode(Long.MIN_VALUE); + case "integer": + return FACTORY.numberNode(Integer.MIN_VALUE); + case "short": + return FACTORY.numberNode(Short.MIN_VALUE); + case "double": + return FACTORY.numberNode(Double.MIN_VALUE); + case "float": + return FACTORY.numberNode(Float.MIN_VALUE); + case "date": + // sentinel for missing dates: 9999-12-31 + final long millisEpoch = LocalDate.of(9999, 12, 31) + .atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli(); + // by default elastic returns dates as longs + return FACTORY.numberNode(millisEpoch); + } + + // this is unknown type + return null; + } + + /** + * Name of the type: {@code text}, {@code integer}, {@code float} etc. + */ + String name() { + return this.name; + } + + Optional<JsonNode> missingValue() { + return Optional.ofNullable(missingValue); + } + + static boolean isMissingValue(JsonNode node) { + return MISSING_VALUES.contains(node); + } + } + +} + +// End ElasticsearchMapping.java http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java index f9565ec..b009fff 100644 --- a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java +++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java @@ -219,15 +219,20 @@ public class ElasticsearchTable extends AbstractQueryableTable implements Transl final ObjectNode section = parent.with(aggName); final ObjectNode terms = section.with("terms"); terms.put("field", name); - terms.set("missing", ElasticsearchJson.MISSING_VALUE); // expose missing terms + + transport.mapping.missingValueFor(name).ifPresent(m -> { + // expose missing terms. each type has a different missing value + terms.set("missing", m); + }); if (fetch != null) { terms.put("size", fetch); } - sort.stream().filter(e -> e.getKey().equals(name)).findAny().ifPresent(s -> { - terms.with("order").put("_key", s.getValue().isDescending() ? "desc" : "asc"); - }); + sort.stream().filter(e -> e.getKey().equals(name)).findAny() + .ifPresent(s -> + terms.with("order") + .put("_key", s.getValue().isDescending() ? "desc" : "asc")); parent = section.with(AGGREGATIONS); } http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTransport.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTransport.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTransport.java index 12173d8..0c9c06a 100644 --- a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTransport.java +++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTransport.java @@ -36,6 +36,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; +import com.google.common.collect.ImmutableMap; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; @@ -72,6 +73,8 @@ final class ElasticsearchTransport { final ElasticsearchVersion version; + final ElasticsearchMapping mapping; + /** * Default batch size * @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html">Scrolling API</a> @@ -89,6 +92,7 @@ final class ElasticsearchTransport { this.typeName = Objects.requireNonNull(typeName, "typeName"); this.fetchSize = fetchSize; this.version = version(); // cache version + this.mapping = fetchAndCreateMapping(); // cache mapping } RestClient restClient() { @@ -112,6 +116,19 @@ final class ElasticsearchTransport { .apply(request); } + /** + * Build index mapping returning new instance of {@link ElasticsearchMapping}. + */ + private ElasticsearchMapping fetchAndCreateMapping() { + final String uri = String.format(Locale.ROOT, "/%s/%s/_mapping", indexName, typeName); + final ObjectNode root = rawHttp(ObjectNode.class).apply(new HttpGet(uri)); + ObjectNode properties = (ObjectNode) root.elements().next().get("mappings").elements().next(); + + ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); + ElasticsearchJson.visitMappingProperties(properties, builder::put); + return new ElasticsearchMapping(indexName, typeName, builder.build()); + } + ObjectMapper mapper() { return mapper; } http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/Scrolling.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/Scrolling.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/Scrolling.java index f947ae5..85850d8 100644 --- a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/Scrolling.java +++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/Scrolling.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.adapter.elasticsearch; import com.fasterxml.jackson.databind.node.ObjectNode; http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java index d06cc7d..0586e88 100644 --- a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java +++ b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationTest.java @@ -23,6 +23,8 @@ import org.apache.calcite.schema.impl.ViewTableMacro; import org.apache.calcite.test.CalciteAssert; import org.apache.calcite.test.ElasticsearchChecker; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.ImmutableMap; @@ -41,7 +43,7 @@ import java.util.Locale; import java.util.Map; /** - * Testing Elastic Search aggregation transformations. + * Testing Elasticsearch aggregation transformations. */ public class AggregationTest { @@ -53,20 +55,29 @@ public class AggregationTest { @BeforeClass public static void setupInstance() throws Exception { - final Map<String, String> mappings = ImmutableMap.of("cat1", "keyword", - "cat2", "keyword", "cat3", "keyword", - "val1", "long", "val2", "long"); + final Map<String, String> mappings = ImmutableMap.<String, String>builder() + .put("cat1", "keyword") + .put("cat2", "keyword") + .put("cat3", "keyword") + .put("cat4", "date") + .put("cat5", "integer") + .put("val1", "long") + .put("val2", "long") + .build(); NODE.createIndex(NAME, mappings); - String doc1 = "{'cat1': 'a', 'cat2': 'g', 'val1': 1 }".replace('\'', '"'); - String doc2 = "{'cat2': 'g', 'cat3': 'y', 'val2': 5 }".replace('\'', '"'); - String doc3 = "{'cat1': 'b', 'cat2':'h', 'cat3': 'z', 'val1': 7, 'val2': '42'}" - .replace('\'', '"'); + String doc1 = "{cat1:'a', cat2:'g', val1:1, cat4:'2018-01-01', cat5:1}"; + String doc2 = "{cat2:'g', cat3:'y', val2:5, cat4:'2019-12-12'}"; + String doc3 = "{cat1:'b', cat2:'h', cat3:'z', cat5:2, val1:7, val2:42}"; - List<ObjectNode> docs = new ArrayList<>(); + final ObjectMapper mapper = new ObjectMapper() + .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) // user-friendly settings to + .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); // avoid too much quoting + + final List<ObjectNode> docs = new ArrayList<>(); for (String text: Arrays.asList(doc1, doc2, doc3)) { - docs.add((ObjectNode) NODE.mapper().readTree(text)); + docs.add((ObjectNode) mapper.readTree(text)); } NODE.insertBulk(NAME, docs); @@ -85,6 +96,8 @@ public class AggregationTest { "select _MAP['cat1'] AS \"cat1\", " + " _MAP['cat2'] AS \"cat2\", " + " _MAP['cat3'] AS \"cat3\", " + + " _MAP['cat4'] AS \"cat4\", " + + " _MAP['cat5'] AS \"cat5\", " + " _MAP['val1'] AS \"val1\", " + " _MAP['val2'] AS \"val2\" " + " from \"elastic\".\"%s\"", NAME); @@ -118,7 +131,7 @@ public class AggregationTest { } @Test - public void all() throws Exception { + public void all() { CalciteAssert.that() .with(newConnectionFactory()) .query("select count(*), sum(val1), sum(val2) from view") @@ -141,7 +154,7 @@ public class AggregationTest { } @Test - public void cat1() throws Exception { + public void cat1() { CalciteAssert.that() .with(newConnectionFactory()) .query("select cat1, sum(val1), sum(val2) from view group by cat1") @@ -173,7 +186,7 @@ public class AggregationTest { } @Test - public void cat2() throws Exception { + public void cat2() { CalciteAssert.that() .with(newConnectionFactory()) .query("select cat2, min(val1), max(val1), min(val2), max(val2) from view group by cat2") @@ -194,7 +207,7 @@ public class AggregationTest { } @Test - public void cat1Cat2() throws Exception { + public void cat1Cat2() { CalciteAssert.that() .with(newConnectionFactory()) .query("select cat1, cat2, sum(val1), sum(val2) from view group by cat1, cat2") @@ -211,7 +224,7 @@ public class AggregationTest { } @Test - public void cat1Cat3() throws Exception { + public void cat1Cat3() { CalciteAssert.that() .with(newConnectionFactory()) .query("select cat1, cat3, sum(val1), sum(val2) from view group by cat1, cat3") @@ -224,7 +237,7 @@ public class AggregationTest { * Testing {@link org.apache.calcite.sql.SqlKind#ANY_VALUE} aggregate function */ @Test - public void anyValue() throws Exception { + public void anyValue() { CalciteAssert.that() .with(newConnectionFactory()) .query("select cat1, any_value(cat2) from view group by cat1") @@ -247,7 +260,7 @@ public class AggregationTest { } @Test - public void cat1Cat2Cat3() throws Exception { + public void cat1Cat2Cat3() { CalciteAssert.that() .with(newConnectionFactory()) .query("select cat1, cat2, cat3, count(*), sum(val1), sum(val2) from view " @@ -256,6 +269,36 @@ public class AggregationTest { "cat1=b; cat2=h; cat3=z; EXPR$3=1; EXPR$4=7.0; EXPR$5=42.0", "cat1=null; cat2=g; cat3=y; EXPR$3=1; EXPR$4=0.0; EXPR$5=5.0"); } + + /** + * Group by + * <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/date.html"> + * date</a> data type. + */ + @Test + public void dateCat() { + CalciteAssert.that() + .with(newConnectionFactory()) + .query("select cat4, sum(val1) from view group by cat4") + .returnsUnordered("cat4=1514764800000; EXPR$1=1.0", + "cat4=1576108800000; EXPR$1=0.0", + "cat4=null; EXPR$1=7.0"); + } + + /** + * Group by + * <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/number.html"> + * number</a> data type. + */ + @Test + public void integerCat() { + CalciteAssert.that() + .with(newConnectionFactory()) + .query("select cat5, sum(val1) from view group by cat5") + .returnsUnordered("cat5=1; EXPR$1=1.0", + "cat5=null; EXPR$1=0.0", + "cat5=2; EXPR$1=7.0"); + } } // End AggregationTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/BooleanLogicTest.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/BooleanLogicTest.java b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/BooleanLogicTest.java index c870234..c3a233a 100644 --- a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/BooleanLogicTest.java +++ b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/BooleanLogicTest.java @@ -54,7 +54,7 @@ public class BooleanLogicTest { @BeforeClass public static void setupInstance() throws Exception { - final Map<String, String> mapping = ImmutableMap.of("A", "keyword", "b", "keyword", + final Map<String, String> mapping = ImmutableMap.of("a", "keyword", "b", "keyword", "c", "keyword", "int", "long"); NODE.createIndex(NAME, mapping); @@ -80,7 +80,8 @@ public class BooleanLogicTest { + " from \"elastic\".\"%s\"", NAME); ViewTableMacro macro = ViewTable.viewMacro(root, viewSql, - Collections.singletonList("elastic"), Arrays.asList("elastic", "view"), false); + Collections.singletonList("elastic"), + Arrays.asList("elastic", "view"), false); root.add("VIEW", macro); return connection; @@ -131,7 +132,7 @@ public class BooleanLogicTest { assertSingle("select * from view where c = 'c' and (a in ('a', 'b') or num in (41, 42))"); assertSingle("select * from view where (a = 'a' or b = 'b') or (num = 42 and c = 'c')"); assertSingle("select * from view where a = 'a' and (b = '0' or (b = 'b' and " - + "(c = '0' or (c = 'c' and num = 42))))"); + + "(c = '0' or (c = 'c' and num = 42))))"); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/ed3da62d/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ScrollingTest.java ---------------------------------------------------------------------- diff --git a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ScrollingTest.java b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ScrollingTest.java index 5beec12..b792e20 100644 --- a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ScrollingTest.java +++ b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ScrollingTest.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.adapter.elasticsearch; import org.apache.calcite.jdbc.CalciteConnection;
