Same failure for me on JDK 8/macOS. The most likely other variable is maven: I have 3.5.2 on macOS, 3.5.3 on ubuntu.
I’m mystified why no one else is seeing javadoc failures on macOS. Julian > On Jun 22, 2018, at 4:35 PM, Julian Hyde <[email protected]> wrote: > > On JDK 8/ubuntu, “mvn clean -DskipTests site” gives the following failure: > > [ERROR] Failed to execute goal > org.apache.maven.plugins:maven-site-plugin:3.7:site (default-site) on project > calcite-mongodb: Error generating maven-javadoc-plugin:3.0.1:test-javadoc > report: > [ERROR] Exit code: 1 - > /home/jhyde/open1/calcite.4/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java:22: > error: cannot find symbol > [ERROR] import org.apache.calcite.test.CalciteAssert; > [ERROR] ^ > [ERROR] symbol: class CalciteAssert > [ERROR] location: package org.apache.calcite.test > [ERROR] > /home/jhyde/open1/calcite.4/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java:137: > error: package CalciteAssert does not exist > [ERROR] private CalciteAssert.AssertThat assertModel(String model) { > [ERROR] ^ > [ERROR] > /home/jhyde/open1/calcite.4/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java:145: > error: package CalciteAssert does not exist > [ERROR] private CalciteAssert.AssertThat assertModel(URL url) { > [ERROR] ^ > [ERROR] > /home/jhyde/open1/calcite.4/mongodb/src/test/java/org/apache/calcite/test/MongoAssertions.java:43: > error: reference not found > [ERROR] /** Similar to {@link CalciteAssert#checkResultUnordered}, but > filters strings > [ERROR] ^ > > > >> On Jun 22, 2018, at 3:46 PM, Andrei Sereda <[email protected]> wrote: >> >> Hmm. Both master and c12cb4b0de work for me. Perhaps the issue is JDK 8 vs >> 9 ? >> >> # JDK8 (oracle) / macOS >> $ mvn clean -DskipTests site >> >> On Fri, Jun 22, 2018 at 5:56 PM Julian Hyde <[email protected]> wrote: >> >>> I just tried it again. The following fails for me (jdk9, ubuntu): >>> >>> $ git checkout c12cb4b0de >>> $ mvn clean -DskipTests site >>> >>> Constructing Javadoc information... >>> 1 error >>> [INFO] >>> ------------------------------------------------------------------------ >>> [INFO] Reactor Summary: >>> [INFO] >>> [INFO] Calcite 1.17.0-SNAPSHOT ............................ FAILURE [02:29 >>> min] >>> [INFO] Calcite Linq4j ..................................... SKIPPED >>> [INFO] Calcite Core ....................................... SKIPPED >>> [INFO] Calcite Cassandra .................................. SKIPPED >>> [INFO] Calcite Druid ...................................... SKIPPED >>> [INFO] Calcite Elasticsearch .............................. SKIPPED >>> [INFO] Calcite Elasticsearch5 ............................. SKIPPED >>> [INFO] Calcite Examples ................................... SKIPPED >>> [INFO] Calcite Example CSV ................................ SKIPPED >>> [INFO] Calcite Example Function ........................... SKIPPED >>> [INFO] Calcite File ....................................... SKIPPED >>> [INFO] Calcite Geode ...................................... SKIPPED >>> [INFO] Calcite MongoDB .................................... SKIPPED >>> [INFO] Calcite Pig ........................................ SKIPPED >>> [INFO] Calcite Piglet ..................................... SKIPPED >>> [INFO] Calcite Plus ....................................... SKIPPED >>> [INFO] Calcite Server ..................................... SKIPPED >>> [INFO] Calcite Spark ...................................... SKIPPED >>> [INFO] Calcite Splunk ..................................... SKIPPED >>> [INFO] Calcite Ubenchmark 1.17.0-SNAPSHOT ................. SKIPPED >>> [INFO] >>> ------------------------------------------------------------------------ >>> [INFO] BUILD FAILURE >>> [INFO] >>> ------------------------------------------------------------------------ >>> [INFO] Total time: 02:29 min >>> [INFO] Finished at: 2018-06-22T14:53:13-07:00 >>> [INFO] >>> ------------------------------------------------------------------------ >>> [ERROR] Failed to execute goal >>> org.apache.maven.plugins:maven-site-plugin:3.7:site (default-site) on >>> project calcite: Error generating maven-javadoc-plugin:3.0.1:test-aggregate >>> report: >>> [ERROR] Exit code: 1 - >>> /home/jhyde/open1/calcite.4/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticNode.java:29: >>> error: package org.elasticsearch.node.internal does not exist >>> [ERROR] import org.elasticsearch.node.internal.InternalSettingsPreparer; >>> [ERROR] ^ >>> [ERROR] >>> [ERROR] Command line was: /usr/lib/jvm/jdk9/bin/javadoc @options @packages >>> @argfile >>> [ERROR] >>> [ERROR] Refer to the generated Javadoc files in >>> '/home/jhyde/open1/calcite.4/target/site/testapidocs' dir. >>> [ERROR] -> [Help 1] >>> [ERROR] >>> [ERROR] To see the full stack trace of the errors, re-run Maven with the >>> -e switch. >>> [ERROR] Re-run Maven using the -X switch to enable full debug logging. >>> [ERROR] >>> [ERROR] For more information about the errors and possible solutions, >>> please read the following articles: >>> [ERROR] [Help 1] >>> http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException >>> >>> >>>> On Jun 22, 2018, at 2:41 PM, Michael Mior <[email protected]> wrote: >>>> >>>> Odd, mvn clean site still works fine for me. >>>> -- >>>> Michael Mior >>>> [email protected] >>>> >>>> >>>> >>>> Le ven. 22 juin 2018 à 17:12, Julian Hyde <[email protected]> a écrit : >>>> >>>>> Looks like this change broke “mvn site” (perhaps also “mvn >>>>> javadoc:test-javadoc”). >>>>> >>>>> [ERROR] Failed to execute goal >>>>> org.apache.maven.plugins:maven-site-plugin:3.7:site (default-site) on >>>>> project calcite: Error generating >>> maven-javadoc-plugin:3.0.1:test-aggregate >>>>> report: >>>>> [ERROR] Exit code: 1 - >>>>> >>> /home/jhyde/regress/calcite/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticNode.java:29: >>>>> error: package org.elasticsearch.node.internal does not exist >>>>> [ERROR] import org.elasticsearch.node.internal.InternalSettingsPreparer; >>>>> [ERROR] ^ >>>>> [ERROR] >>>>> >>>>>> On Jun 21, 2018, at 3:39 AM, [email protected] wrote: >>>>>> >>>>>> [CALCITE-2347] running ElasticSearch in embedded mode for unit tests of >>>>> ES adapter (Andrei Sereda) >>>>>> >>>>>> After discussion on dev-list Integration tests (for ES) have been >>>>> removed. They're now >>>>>> superseded by unit tests (which execute queries against a real elastic >>>>> instance) >>>>>> >>>>>> Added local file (zips-mini.json) which contains a small subset of >>>>> original zips.json >>>>>> (allows to bootstrap tests faster) >>>>>> >>>>>> Created separate ES JUnit rule which can be re-used across different >>>>> tests. >>>>>> >>>>>> Both v2 and v5 of ES adapters are supported. >>>>>> >>>>>> Close apache/calcite#716 >>>>>> >>>>>> >>>>>> Project: http://git-wip-us.apache.org/repos/asf/calcite/repo >>>>>> Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/c12cb4b0 >>>>>> Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/c12cb4b0 >>>>>> Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/c12cb4b0 >>>>>> >>>>>> Branch: refs/heads/master >>>>>> Commit: c12cb4b0de1baa3f7cbb9952ee350fdd1701662d >>>>>> Parents: 37944bb >>>>>> Author: Andrei Sereda <[email protected]> >>>>>> Authored: Thu May 31 18:19:10 2018 -0400 >>>>>> Committer: Michael Mior <[email protected]> >>>>>> Committed: Thu Jun 21 06:38:50 2018 -0400 >>>>>> >>>>>> ---------------------------------------------------------------------- >>>>>> .../AbstractElasticsearchTable.java | 12 + >>>>>> .../elasticsearch/ElasticsearchProject.java | 61 ++- >>>>>> elasticsearch2/pom.xml | 6 + >>>>>> .../Elasticsearch2Enumerator.java | 12 +- >>>>>> .../elasticsearch2/Elasticsearch2Schema.java | 16 +- >>>>>> .../elasticsearch2/Elasticsearch2Table.java | 9 +- >>>>>> .../ElasticSearch2AdapterTest.java | 395 >>> ++++++++++++++++++ >>>>>> .../elasticsearch2/EmbeddedElasticNode.java | 147 +++++++ >>>>>> .../elasticsearch2/EmbeddedElasticRule.java | 97 +++++ >>>>>> .../org/apache/calcite/test/ElasticChecker.java | 49 +++ >>>>>> .../calcite/test/Elasticsearch2AdapterIT.java | 270 ------------- >>>>>> .../resources/elasticsearch-zips-model.json | 50 --- >>>>>> .../src/test/resources/zips-mini.json | 149 +++++++ >>>>>> elasticsearch5/pom.xml | 31 ++ >>>>>> .../elasticsearch5/Elasticsearch5Schema.java | 17 +- >>>>>> .../elasticsearch5/Elasticsearch5Table.java | 11 +- >>>>>> .../ElasticSearch5AdapterTest.java | 399 >>> +++++++++++++++++++ >>>>>> .../elasticsearch5/EmbeddedElasticNode.java | 153 +++++++ >>>>>> .../elasticsearch5/EmbeddedElasticRule.java | 98 +++++ >>>>>> .../org/apache/calcite/test/ElasticChecker.java | 49 +++ >>>>>> .../calcite/test/Elasticsearch5AdapterIT.java | 270 ------------- >>>>>> .../resources/elasticsearch-zips-model.json | 50 --- >>>>>> elasticsearch5/src/test/resources/log4j2.xml | 16 + >>>>>> .../src/test/resources/zips-mini.json | 149 +++++++ >>>>>> pom.xml | 20 +- >>>>>> 25 files changed, 1866 insertions(+), 670 deletions(-) >>>>>> ---------------------------------------------------------------------- >>>>>> >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/core/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java >>>>> >>> b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java >>>>>> index 0980469..8cc5933 100644 >>>>>> --- >>>>> >>> a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java >>>>>> +++ >>>>> >>> b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java >>>>>> @@ -75,6 +75,18 @@ public abstract class AbstractElasticsearchTable >>>>> extends AbstractQueryableTable >>>>>> relOptTable, this, null); >>>>>> } >>>>>> >>>>>> + /** >>>>>> + * In ES 5.x scripted fields start with {@code params._source.foo} >>>>> while in ES2.x >>>>>> + * {@code _source.foo}. Helper method to build correct query based >>> on >>>>> runtime version of elastic. >>>>>> + * >>>>>> + * @see <a href=" >>>>> https://github.com/elastic/elasticsearch/issues/20068">_source >>>>> variable</a> >>>>>> + * @see <a href=" >>>>> >>> https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-scripting-fields.html >>> ">Scripted >>>>> Fields</a> >>>>>> + */ >>>>>> + protected String scriptedFieldPrefix() { >>>>>> + // this is default pattern starting 5.x >>>>>> + return "params._source"; >>>>>> + } >>>>>> + >>>>>> /** Executes a "find" operation on the underlying type. >>>>>> * >>>>>> * <p>For example, >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java >>>>> >>> b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java >>>>>> index b42abd7..961c8b0 100644 >>>>>> --- >>>>> >>> a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java >>>>>> +++ >>>>> >>> b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java >>>>>> @@ -27,11 +27,15 @@ import >>>>> org.apache.calcite.rel.metadata.RelMetadataQuery; >>>>>> import org.apache.calcite.rel.type.RelDataType; >>>>>> import org.apache.calcite.rex.RexNode; >>>>>> import org.apache.calcite.util.Pair; >>>>>> -import org.apache.calcite.util.Util; >>>>>> + >>>>>> +import com.google.common.base.Function; >>>>>> +import com.google.common.collect.Lists; >>>>>> >>>>>> import java.util.ArrayList; >>>>>> import java.util.List; >>>>>> >>>>>> +import javax.annotation.Nullable; >>>>>> + >>>>>> /** >>>>>> * Implementation of {@link org.apache.calcite.rel.core.Project} >>>>>> * relational expression in Elasticsearch. >>>>>> @@ -57,41 +61,60 @@ public class ElasticsearchProject extends Project >>>>> implements ElasticsearchRel { >>>>>> implementor.visitChild(0, getInput()); >>>>>> >>>>>> final List<String> inFields = >>>>>> - >>>>> ElasticsearchRules.elasticsearchFieldNames(getInput().getRowType()); >>>>>> + >>>>> ElasticsearchRules.elasticsearchFieldNames(getInput().getRowType()); >>>>>> final ElasticsearchRules.RexToElasticsearchTranslator translator = >>>>>> - new ElasticsearchRules.RexToElasticsearchTranslator( >>>>>> - (JavaTypeFactory) getCluster().getTypeFactory(), >>> inFields); >>>>>> + new ElasticsearchRules.RexToElasticsearchTranslator( >>>>>> + (JavaTypeFactory) getCluster().getTypeFactory(), >>>>> inFields); >>>>>> >>>>>> - final List<String> findItems = new ArrayList<>(); >>>>>> - final List<String> scriptFieldItems = new ArrayList<>(); >>>>>> + final List<String> fields = new ArrayList<>(); >>>>>> + final List<String> scriptFields = new ArrayList<>(); >>>>>> for (Pair<RexNode, String> pair: getNamedProjects()) { >>>>>> final String name = pair.right; >>>>>> final String expr = pair.left.accept(translator); >>>>>> >>>>>> if (expr.equals("\"" + name + "\"")) { >>>>>> - findItems.add(ElasticsearchRules.quote(name)); >>>>>> + fields.add(name); >>>>>> } else if (expr.matches("\"literal\":.+")) { >>>>>> - scriptFieldItems.add(ElasticsearchRules.quote(name) >>>>>> - + ":{\"script\": " >>>>>> - + expr.split(":")[1] + "}"); >>>>>> + scriptFields.add(ElasticsearchRules.quote(name) >>>>>> + + ":{\"script\": " >>>>>> + + expr.split(":")[1] + "}"); >>>>>> } else { >>>>>> - scriptFieldItems.add(ElasticsearchRules.quote(name) >>>>>> - + ":{\"script\":\"params._source." >>>>>> - + expr.replaceAll("\"", "") + "\"}"); >>>>>> + scriptFields.add(ElasticsearchRules.quote(name) >>>>>> + + ":{\"script\":" >>>>>> + // _source (ES2) vs params._source (ES5) >>>>>> + + "\"" + >>>>> implementor.elasticsearchTable.scriptedFieldPrefix() + "." >>>>>> + + expr.replaceAll("\"", "") + "\"}"); >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + StringBuilder query = new StringBuilder(); >>>>>> + if (scriptFields.isEmpty()) { >>>>>> + List<String> newList = Lists.transform(fields, new >>>>> Function<String, String>() { >>>>>> + @Nullable >>>>>> + @Override public String apply(@Nullable String input) { >>>>>> + return ElasticsearchRules.quote(input); >>>>>> + } >>>>>> + }); >>>>>> + >>>>>> + final String findString = String.join(", ", newList); >>>>>> + query.append("\"_source\" : [").append(findString).append("]"); >>>>>> + } else { >>>>>> + // if scripted fields are present, ES ignores _source attribute >>>>>> + for (String field: fields) { >>>>>> + scriptFields.add(ElasticsearchRules.quote(field) + >>>>> ":{\"script\": " >>>>>> + // _source (ES2) vs params._source (ES5) >>>>>> + + "\"" + >>>>> implementor.elasticsearchTable.scriptedFieldPrefix() + "." >>>>>> + + field + "\"}"); >>>>>> } >>>>>> + query.append("\"script_fields\": {" + String.join(", ", >>>>> scriptFields) + "}"); >>>>>> } >>>>>> - final String findString = Util.toString(findItems, "", ", ", ""); >>>>>> - final String scriptFieldString = "\"script_fields\": {" >>>>>> - + Util.toString(scriptFieldItems, "", ", ", "") + "}"; >>>>>> - final String fieldString = "\"_source\" : [" + findString + "]" >>>>>> - + ", " + scriptFieldString; >>>>>> >>>>>> for (String opfield : implementor.list) { >>>>>> if (opfield.startsWith("\"_source\"")) { >>>>>> implementor.list.remove(opfield); >>>>>> } >>>>>> } >>>>>> - implementor.add(fieldString); >>>>>> + implementor.add(query.toString()); >>>>>> } >>>>>> } >>>>>> >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/pom.xml >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git a/elasticsearch2/pom.xml b/elasticsearch2/pom.xml >>>>>> index f24622c..6fbee03 100644 >>>>>> --- a/elasticsearch2/pom.xml >>>>>> +++ b/elasticsearch2/pom.xml >>>>>> @@ -73,6 +73,12 @@ limitations under the License. >>>>>> <version>${elasticsearch-java-driver.version}</version> >>>>>> </dependency> >>>>>> <dependency> >>>>>> + <!-- Lang groovy dependency is needed for testing with embedded >>>>> ES (scripted fields like loc[0]) --> >>>>>> + <groupId>org.elasticsearch.module</groupId> >>>>>> + <artifactId>lang-groovy</artifactId> >>>>>> + <scope>test</scope> >>>>>> + </dependency> >>>>>> + <dependency> >>>>>> <groupId>com.carrotsearch</groupId> >>>>>> <artifactId>hppc</artifactId> >>>>>> <version>${hppc.version}</version> >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Enumerator.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Enumerator.java >>>>> >>> b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Enumerator.java >>>>>> index 84370ab..c3d2ac0 100644 >>>>>> --- >>>>> >>> a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Enumerator.java >>>>>> +++ >>>>> >>> b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Enumerator.java >>>>>> @@ -26,6 +26,7 @@ import org.elasticsearch.search.SearchHit; >>>>>> import java.util.Date; >>>>>> import java.util.Iterator; >>>>>> import java.util.List; >>>>>> +import java.util.Locale; >>>>>> import java.util.Map; >>>>>> >>>>>> /** >>>>>> @@ -101,15 +102,18 @@ public class Elasticsearch2Enumerator implements >>>>> Enumerator<Object> { >>>>>> private static Function1<SearchHit, Object[]> listGetter( >>>>>> final List<Map.Entry<String, Class>> fields) { >>>>>> return new Function1<SearchHit, Object[]>() { >>>>>> - public Object[] apply(SearchHit searchHitFields) { >>>>>> + public Object[] apply(SearchHit hit) { >>>>>> Object[] objects = new Object[fields.size()]; >>>>>> for (int i = 0; i < fields.size(); i++) { >>>>>> final Map.Entry<String, Class> field = fields.get(i); >>>>>> final String name = field.getKey(); >>>>>> - if (searchHitFields.fields().isEmpty()) { >>>>>> - objects[i] = >>> convert(searchHitFields.getSource().get(name), >>>>> field.getValue()); >>>>>> + if (hit.fields().isEmpty()) { >>>>>> + objects[i] = convert(hit.getSource().get(name), >>>>> field.getValue()); >>>>>> + } else if (hit.fields().containsKey(name)) { >>>>>> + objects[i] = convert(hit.field(name).getValue(), >>>>> field.getValue()); >>>>>> } else { >>>>>> - objects[i] = >>>>> convert(searchHitFields.field(name).getValue(), field.getValue()); >>>>>> + throw new IllegalStateException( >>>>>> + String.format(Locale.getDefault(), "No result for >>>>> %s", field)); >>>>>> } >>>>>> } >>>>>> return objects; >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Schema.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Schema.java >>>>> >>> b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Schema.java >>>>>> index 668402b..46e3fc5 100644 >>>>>> --- >>>>> >>> a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Schema.java >>>>>> +++ >>>>> >>> b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Schema.java >>>>>> @@ -22,6 +22,9 @@ import org.apache.calcite.schema.impl.AbstractSchema; >>>>>> >>>>>> import com.carrotsearch.hppc.cursors.ObjectObjectCursor; >>>>>> >>>>>> +import com.google.common.annotations.VisibleForTesting; >>>>>> +import com.google.common.base.Preconditions; >>>>>> + >>>>>> import com.google.common.collect.ImmutableList; >>>>>> import com.google.common.collect.ImmutableMap; >>>>>> >>>>>> @@ -86,6 +89,16 @@ public class Elasticsearch2Schema extends >>>>> AbstractSchema >>>>>> } >>>>>> } >>>>>> >>>>>> + /** >>>>>> + * Allows schema to be instantiated from existing elastic search >>>>> client. >>>>>> + * This constructor is used in tests. >>>>>> + */ >>>>>> + @VisibleForTesting >>>>>> + Elasticsearch2Schema(Client client, String index) { >>>>>> + this.client = Preconditions.checkNotNull(client, "client"); >>>>>> + this.index = Preconditions.checkNotNull(index, "index"); >>>>>> + } >>>>>> + >>>>>> @Override protected Map<String, Table> getTableMap() { >>>>>> final ImmutableMap.Builder<String, Table> builder = >>>>> ImmutableMap.builder(); >>>>>> >>>>>> @@ -120,7 +133,8 @@ public class Elasticsearch2Schema extends >>>>> AbstractSchema >>>>>> >>>>>> final List<DiscoveryNode> nodes = >>>>> ImmutableList.copyOf(transportClient.connectedNodes()); >>>>>> if (nodes.isEmpty()) { >>>>>> - throw new RuntimeException("Cannot connect to any elasticsearch >>>>> nodes"); >>>>>> + throw new IllegalStateException("Cannot connect to any >>>>> elasticsearch node: " >>>>>> + + transportNodes); >>>>>> } >>>>>> >>>>>> client = transportClient; >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Table.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Table.java >>>>> >>> b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Table.java >>>>>> index 636aa5f..2928835 100644 >>>>>> --- >>>>> >>> a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Table.java >>>>>> +++ >>>>> >>> b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Table.java >>>>>> @@ -45,8 +45,15 @@ public class Elasticsearch2Table extends >>>>> AbstractElasticsearchTable { >>>>>> this.client = client; >>>>>> } >>>>>> >>>>>> + /** >>>>>> + * ES version 2.x. To access document attributes ES2 uses {@code >>>>> _source.foo} syntax. >>>>>> + */ >>>>>> + @Override protected String scriptedFieldPrefix() { >>>>>> + return "_source"; >>>>>> + } >>>>>> + >>>>>> @Override protected Enumerable<Object> find(String index, List<String> >>>>> ops, >>>>>> - List<Map.Entry<String, Class>> fields) { >>>>>> + List<Map.Entry<String, >>>>> Class>> fields) { >>>>>> final String dbName = index; >>>>>> >>>>>> final String queryString = "{" + Util.toString(ops, "", ", ", "") + >>>>> "}"; >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/ElasticSearch2AdapterTest.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/ElasticSearch2AdapterTest.java >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/ElasticSearch2AdapterTest.java >>>>>> new file mode 100644 >>>>>> index 0000000..287e094 >>>>>> --- /dev/null >>>>>> +++ >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/ElasticSearch2AdapterTest.java >>>>>> @@ -0,0 +1,395 @@ >>>>>> +/* >>>>>> + * 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.elasticsearch2; >>>>>> + >>>>>> +import org.apache.calcite.jdbc.CalciteConnection; >>>>>> +import org.apache.calcite.schema.SchemaPlus; >>>>>> +import org.apache.calcite.schema.impl.ViewTable; >>>>>> +import org.apache.calcite.schema.impl.ViewTableMacro; >>>>>> +import org.apache.calcite.test.CalciteAssert; >>>>>> +import org.apache.calcite.test.ElasticChecker; >>>>>> + >>>>>> +import com.google.common.io.LineProcessor; >>>>>> +import com.google.common.io.Resources; >>>>>> + >>>>>> +import org.elasticsearch.action.bulk.BulkItemResponse; >>>>>> +import org.elasticsearch.action.bulk.BulkRequestBuilder; >>>>>> +import org.elasticsearch.action.bulk.BulkResponse; >>>>>> +import org.elasticsearch.common.xcontent.XContentBuilder; >>>>>> +import org.elasticsearch.common.xcontent.XContentFactory; >>>>>> + >>>>>> +import org.junit.BeforeClass; >>>>>> +import org.junit.ClassRule; >>>>>> +import org.junit.Test; >>>>>> + >>>>>> +import java.io.IOException; >>>>>> +import java.nio.charset.StandardCharsets; >>>>>> +import java.sql.Connection; >>>>>> +import java.sql.DriverManager; >>>>>> +import java.sql.SQLException; >>>>>> +import java.util.Arrays; >>>>>> +import java.util.Collections; >>>>>> +import java.util.Locale; >>>>>> + >>>>>> +/** >>>>>> + * Set of tests for ES adapter. Uses real instance via {@link >>>>> EmbeddedElasticRule}. Document >>>>>> + * source is local {@code zips-mini.json} file (located in the >>>>> classpath). >>>>>> + */ >>>>>> +public class ElasticSearch2AdapterTest { >>>>>> + >>>>>> + @ClassRule //init once for all tests >>>>>> + public static final EmbeddedElasticRule NODE = >>>>> EmbeddedElasticRule.create(); >>>>>> + >>>>>> + private static final String ZIPS = "zips"; >>>>>> + >>>>>> + /** >>>>>> + * Used to create {@code zips} index and insert some data >>>>>> + */ >>>>>> + @BeforeClass >>>>>> + public static void setupInstance() throws Exception { >>>>>> + // define mapping so fields are searchable (term query) >>>>>> + XContentBuilder mapping = >>>>> XContentFactory.jsonBuilder().startObject() >>>>>> + .startObject("properties") >>>>>> + .startObject("city").field("type", "string") >>>>>> + .field("index", "not_analyzed").endObject() >>>>>> + .startObject("state").field("type", "string") >>>>>> + .field("index", "not_analyzed").endObject() >>>>>> + .startObject("pop").field("type", "long").endObject() >>>>>> + .endObject() >>>>>> + .endObject(); >>>>>> + >>>>>> + // create index >>>>>> + NODE.client().admin().indices() >>>>>> + .prepareCreate(ZIPS) >>>>>> + .addMapping(ZIPS, mapping) >>>>>> + .get(); >>>>>> + >>>>>> + BulkRequestBuilder bulk = >>>>> NODE.client().prepareBulk().setRefresh(true); >>>>>> + >>>>>> + // load records from file >>>>>> + >>>>> >>> Resources.readLines(ElasticSearch2AdapterTest.class.getResource("/zips-mini.json"), >>>>>> + StandardCharsets.UTF_8, new LineProcessor<Void>() { >>>>>> + @Override public boolean processLine(String line) throws >>>>> IOException { >>>>>> + line = line.replaceAll("_id", "id"); // _id is a >>>>> reserved attribute in ES >>>>>> + bulk.add(NODE.client().prepareIndex(ZIPS, >>>>> ZIPS).setSource(line)); >>>>>> + return true; >>>>>> + } >>>>>> + >>>>>> + @Override public Void getResult() { >>>>>> + return null; >>>>>> + } >>>>>> + }); >>>>>> + >>>>>> + if (bulk.numberOfActions() == 0) { >>>>>> + throw new IllegalStateException("No records to be indexed"); >>>>>> + } >>>>>> + >>>>>> + BulkResponse response = bulk.execute().get(); >>>>>> + >>>>>> + if (response.hasFailures()) { >>>>>> + throw new IllegalStateException( >>>>>> + String.format(Locale.getDefault(), "Failed to populate >>>>> %s:\n%s", NODE.httpAddress(), >>>>>> + >>>>> Arrays.stream(response.getItems()).filter(BulkItemResponse::isFailed) >>>>>> + >>>>> >>> .map(BulkItemResponse::getFailureMessage).findFirst().orElse("<unknown>"))); >>>>>> + } >>>>>> + >>>>>> + } >>>>>> + >>>>>> + private CalciteAssert.ConnectionFactory newConnectionFactory() { >>>>>> + return new CalciteAssert.ConnectionFactory() { >>>>>> + @Override public Connection createConnection() throws >>>>> SQLException { >>>>>> + final Connection connection = >>>>> DriverManager.getConnection("jdbc:calcite:"); >>>>>> + final SchemaPlus root = >>>>> connection.unwrap(CalciteConnection.class).getRootSchema(); >>>>>> + >>>>>> + root.add("elastic", new Elasticsearch2Schema(NODE.client(), >>>>> ZIPS)); >>>>>> + >>>>>> + // add calcite view programmatically >>>>>> + final String viewSql = "select cast(_MAP['city'] AS >>>>> varchar(20)) AS \"city\", " >>>>>> + + " cast(_MAP['loc'][0] AS float) AS \"longitude\",\n" >>>>>> + + " cast(_MAP['loc'][1] AS float) AS \"latitude\",\n" >>>>>> + + " cast(_MAP['pop'] AS integer) AS \"pop\", " >>>>>> + + " cast(_MAP['state'] AS varchar(2)) AS \"state\", " >>>>>> + + " cast(_MAP['id'] AS varchar(5)) AS \"id\" " >>>>>> + + "from \"elastic\".\"zips\""; >>>>>> + >>>>>> + ViewTableMacro macro = ViewTable.viewMacro(root, viewSql, >>>>>> + Collections.singletonList("elastic"), >>>>> Arrays.asList("elastic", "view"), false); >>>>>> + root.add("ZIPS", macro); >>>>>> + >>>>>> + return connection; >>>>>> + } >>>>>> + }; >>>>>> + } >>>>>> + >>>>>> + private CalciteAssert.AssertThat calciteAssert() { >>>>>> + return CalciteAssert.that() >>>>>> + .with(newConnectionFactory()); >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Tests using calcite view >>>>>> + */ >>>>>> + @Test >>>>>> + public void view() throws Exception { >>>>>> + calciteAssert() >>>>>> + .query("select * from zips where \"city\" = 'BROOKLYN'") >>>>>> + .returns("city=BROOKLYN; longitude=-73.956985; >>>>> latitude=40.646694; " >>>>>> + + "pop=111396; state=NY; id=11226\n") >>>>>> + .returnsCount(1); >>>>>> + } >>>>>> + >>>>>> + @Test >>>>>> + public void emptyResult() { >>>>>> + CalciteAssert.that() >>>>>> + .with(newConnectionFactory()) >>>>>> + .query("select * from zips limit 0") >>>>>> + .returnsCount(0); >>>>>> + >>>>>> + CalciteAssert.that() >>>>>> + .with(newConnectionFactory()) >>>>>> + .query("select * from \"elastic\".\"zips\" where >>>>> _MAP['Foo'] = '_MISSING_'") >>>>>> + .returnsCount(0); >>>>>> + } >>>>>> + >>>>>> + @Test >>>>>> + public void basic() throws Exception { >>>>>> + CalciteAssert.that() >>>>>> + .with(newConnectionFactory()) >>>>>> + .query("select * from \"elastic\".\"zips\" where >>>>> _MAP['city'] = 'BROOKLYN'") >>>>>> + .returnsCount(1); >>>>>> + >>>>>> + CalciteAssert.that() >>>>>> + .with(newConnectionFactory()) >>>>>> + .query("select * from \"elastic\".\"zips\" where" >>>>>> + + " _MAP['city'] in ('BROOKLYN', 'WASHINGTON')") >>>>>> + .returnsCount(2); >>>>>> + >>>>>> + // lower-case >>>>>> + CalciteAssert.that() >>>>>> + .with(newConnectionFactory()) >>>>>> + .query("select * from \"elastic\".\"zips\" where " >>>>>> + + "_MAP['city'] in ('brooklyn', 'Brooklyn', >>>>> 'BROOK') ") >>>>>> + .returnsCount(0); >>>>>> + >>>>>> + // missing field >>>>>> + CalciteAssert.that() >>>>>> + .with(newConnectionFactory()) >>>>>> + .query("select * from \"elastic\".\"zips\" where >>>>> _MAP['CITY'] = 'BROOKLYN'") >>>>>> + .returnsCount(0); >>>>>> + >>>>>> + // limit works >>>>>> + CalciteAssert.that() >>>>>> + .with(newConnectionFactory()) >>>>>> + .query("select * from \"elastic\".\"zips\" limit 42") >>>>>> + .returnsCount(42); >>>>>> + >>>>>> + } >>>>>> + >>>>>> + @Test public void testSort() { >>>>>> + final String explain = "PLAN=ElasticsearchToEnumerableConverter\n" >>>>>> + + " ElasticsearchSort(sort0=[$4], dir0=[ASC])\n" >>>>>> + + " ElasticsearchProject(city=[CAST(ITEM($0, >>>>> 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE >>>>> \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), >>>>> 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], >>>>> pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, >>>>> 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE >>>>> \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) >>>>> CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n" >>>>>> + + " ElasticsearchTableScan(table=[[elastic, zips]])"; >>>>>> + >>>>>> + calciteAssert() >>>>>> + .query("select * from zips order by \"state\"") >>>>>> + .returnsCount(10) >>>>>> + .explainContains(explain); >>>>>> + } >>>>>> + >>>>>> + @Test public void testSortLimit() { >>>>>> + final String sql = "select \"state\", \"id\" from zips\n" >>>>>> + + "order by \"state\", \"id\" offset 2 rows fetch next 3 >>>>> rows only"; >>>>>> + calciteAssert() >>>>>> + .query(sql) >>>>>> + .returnsUnordered("state=AK; id=99801", >>>>>> + "state=AL; id=35215", >>>>>> + "state=AL; id=35401") >>>>>> + .queryContains( >>>>>> + ElasticChecker.elasticsearchChecker( >>>>>> + "\"_source\" : [\"state\", \"id\"]", >>>>>> + "\"sort\": [ {\"state\": \"asc\"}, >>> {\"id\": >>>>> \"asc\"}]", >>>>>> + "\"from\": 2", >>>>>> + "\"size\": 3")); >>>>>> + } >>>>>> + >>>>>> + >>>>>> + >>>>>> + @Test public void testOffsetLimit() { >>>>>> + final String sql = "select \"state\", \"id\" from zips\n" >>>>>> + + "offset 2 fetch next 3 rows only"; >>>>>> + calciteAssert() >>>>>> + .query(sql) >>>>>> + .runs() >>>>>> + .queryContains( >>>>>> + ElasticChecker.elasticsearchChecker( >>>>>> + "\"from\": 2", >>>>>> + "\"size\": 3", >>>>>> + "\"_source\" : [\"state\", \"id\"]")); >>>>>> + } >>>>>> + >>>>>> + @Test public void testLimit() { >>>>>> + final String sql = "select \"state\", \"id\" from zips\n" >>>>>> + + "fetch next 3 rows only"; >>>>>> + >>>>>> + calciteAssert() >>>>>> + .query(sql) >>>>>> + .runs() >>>>>> + .queryContains( >>>>>> + ElasticChecker.elasticsearchChecker( >>>>>> + "\"size\": 3", >>>>>> + "\"_source\" : [\"state\", \"id\"]")); >>>>>> + } >>>>>> + >>>>>> + @Test public void testFilterSort() { >>>>>> + final String sql = "select * from zips\n" >>>>>> + + "where \"state\" = 'CA' and \"id\" >= '70000'\n" >>>>>> + + "order by \"state\", \"id\""; >>>>>> + final String explain = "PLAN=ElasticsearchToEnumerableConverter\n" >>>>>> + + " ElasticsearchSort(sort0=[$4], sort1=[$5], dir0=[ASC], >>>>> dir1=[ASC])\n" >>>>>> + + " ElasticsearchProject(city=[CAST(ITEM($0, >>>>> 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE >>>>> \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), >>>>> 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], >>>>> pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, >>>>> 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE >>>>> \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) >>>>> CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n" >>>>>> + + " >>> ElasticsearchFilter(condition=[AND(=(CAST(ITEM($0, >>>>> 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE >>>>> \"ISO-8859-1$en_US$primary\", 'CA'), >=(CAST(ITEM($0, 'id')):VARCHAR(5) >>>>> CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", >>>>> '70000'))])\n" >>>>>> + + " ElasticsearchTableScan(table=[[elastic, >>> zips]])"; >>>>>> + calciteAssert() >>>>>> + .query(sql) >>>>>> + .returnsOrdered("city=LOS ANGELES; longitude=-118.258189; >>>>> latitude=34.007856; " >>>>>> + + "pop=96074; state=CA; id=90011", >>>>>> + "city=BELL GARDENS; longitude=-118.17205; >>>>> latitude=33.969177; " >>>>>> + + "pop=99568; state=CA; id=90201", >>>>>> + "city=NORWALK; longitude=-118.081767; >>>>> latitude=33.90564; " >>>>>> + + "pop=94188; state=CA; id=90650") >>>>>> + .queryContains( >>>>>> + ElasticChecker.elasticsearchChecker("\"query\" : " >>>>>> + + >>>>> "{\"constant_score\":{\"filter\":{\"bool\":" >>>>>> + + >>>>> "{\"must\":[{\"term\":{\"state\":\"CA\"}}," >>>>>> + + >>>>> "{\"range\":{\"id\":{\"gte\":\"70000\"}}}]}}}}", >>>>>> + "\"script_fields\": >>>>> {\"longitude\":{\"script\":\"_source.loc[0]\"}, " >>>>>> + + >>>>> "\"latitude\":{\"script\":\"_source.loc[1]\"}, " >>>>>> + + "\"city\":{\"script\": >>>>> \"_source.city\"}, " >>>>>> + + "\"pop\":{\"script\": >>>>> \"_source.pop\"}, " >>>>>> + + "\"state\":{\"script\": >>>>> \"_source.state\"}, " >>>>>> + + "\"id\":{\"script\": \"_ >>> source.id >>>>> \"}}", >>>>>> + "\"sort\": [ {\"state\": \"asc\"}, >>> {\"id\": >>>>> \"asc\"}]")) >>>>>> + .explainContains(explain); >>>>>> + } >>>>>> + >>>>>> + @Test public void testFilterSortDesc() { >>>>>> + final String sql = "select * from zips\n" >>>>>> + + "where \"pop\" BETWEEN 95000 AND 100000\n" >>>>>> + + "order by \"state\" desc, \"pop\""; >>>>>> + calciteAssert() >>>>>> + .query(sql) >>>>>> + .limit(4) >>>>>> + .returnsOrdered( >>>>>> + "city=LOS ANGELES; longitude=-118.258189; >>>>> latitude=34.007856; pop=96074; state=CA; id=90011", >>>>>> + "city=BELL GARDENS; longitude=-118.17205; >>>>> latitude=33.969177; pop=99568; state=CA; id=90201"); >>>>>> + } >>>>>> + >>>>>> + @Test public void testFilterRedundant() { >>>>>> + final String sql = "select * from zips\n" >>>>>> + + "where \"state\" > 'CA' and \"state\" < 'AZ' and >>>>> \"state\" = 'OK'"; >>>>>> + calciteAssert() >>>>>> + .query(sql) >>>>>> + .runs() >>>>>> + .queryContains( >>>>>> + ElasticChecker.elasticsearchChecker("" >>>>>> + + "\"query\" : >>>>> {\"constant_score\":{\"filter\":{\"bool\":" >>>>>> + + >>>>> "{\"must\":[{\"term\":{\"state\":\"OK\"}}]}}}}", >>>>>> + "\"script_fields\": >>>>> {\"longitude\":{\"script\":\"_source.loc[0]\"}, " >>>>>> + + >>>>> "\"latitude\":{\"script\":\"_source.loc[1]\"}, " >>>>>> + + "\"city\":{\"script\": >>>>> \"_source.city\"}, " >>>>>> + + "\"pop\":{\"script\": >>>>> \"_source.pop\"}, \"state\":{\"script\": \"_source.state\"}, " >>>>>> + + "\"id\":{\"script\": >>>>> \"_source.id\"}}" >>>>>> + )); >>>>>> + } >>>>>> + >>>>>> + @Test public void testInPlan() { >>>>>> + final String[] searches = { >>>>>> + "\"query\" : >>>>> {\"constant_score\":{\"filter\":{\"bool\":{\"should\":" >>>>>> + + >>>>> >>> "[{\"bool\":{\"must\":[{\"term\":{\"pop\":96074}}]}},{\"bool\":{\"must\":[{\"term\":" >>>>>> + + "{\"pop\":99568}}]}}]}}}}", >>>>>> + "\"script_fields\": >>>>> {\"longitude\":{\"script\":\"_source.loc[0]\"}, " >>>>>> + + "\"latitude\":{\"script\":\"_source.loc[1]\"}, " >>>>>> + + "\"city\":{\"script\": \"_source.city\"}, " >>>>>> + + "\"pop\":{\"script\": \"_source.pop\"}, " >>>>>> + + "\"state\":{\"script\": \"_source.state\"}, " >>>>>> + + "\"id\":{\"script\": \"_source.id\"}}" >>>>>> + }; >>>>>> + >>>>>> + calciteAssert() >>>>>> + .query("select * from zips where \"pop\" in (96074, >>> 99568)") >>>>>> + .returnsUnordered( >>>>>> + "city=BELL GARDENS; longitude=-118.17205; >>>>> latitude=33.969177; pop=99568; state=CA; id=90201", >>>>>> + "city=LOS ANGELES; longitude=-118.258189; >>>>> latitude=34.007856; pop=96074; state=CA; id=90011" >>>>>> + ) >>>>>> + >>>>> .queryContains(ElasticChecker.elasticsearchChecker(searches)); >>>>>> + } >>>>>> + >>>>>> + @Test public void testZips() { >>>>>> + calciteAssert() >>>>>> + .query("select \"state\", \"city\" from zips") >>>>>> + .returnsCount(10); >>>>>> + } >>>>>> + >>>>>> + @Test public void testProject() { >>>>>> + final String sql = "select \"state\", \"city\", 0 as \"zero\"\n" >>>>>> + + "from zips\n" >>>>>> + + "order by \"state\", \"city\""; >>>>>> + >>>>>> + calciteAssert() >>>>>> + .query(sql) >>>>>> + .limit(2) >>>>>> + .returnsUnordered("state=AK; city=ANCHORAGE; zero=0", >>>>>> + "state=AK; city=FAIRBANKS; zero=0") >>>>>> + .queryContains( >>>>>> + >>>>> ElasticChecker.elasticsearchChecker("\"script_fields\": " >>>>>> + + "{\"zero\":{\"script\": \"0\"}, >>> " >>>>>> + + "\"state\":{\"script\": >>>>> \"_source.state\"}, " >>>>>> + + "\"city\":{\"script\": >>>>> \"_source.city\"}}", >>>>>> + "\"sort\": [ {\"state\": \"asc\"}, >>>>> {\"city\": \"asc\"}]")); >>>>>> + } >>>>>> + >>>>>> + @Test public void testFilter() { >>>>>> + final String explain = "PLAN=ElasticsearchToEnumerableConverter\n" >>>>>> + + " ElasticsearchProject(state=[CAST(ITEM($0, >>>>> 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE >>>>> \"ISO-8859-1$en_US$primary\"], city=[CAST(ITEM($0, 'city')):VARCHAR(20) >>>>> CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n" >>>>>> + + " ElasticsearchFilter(condition=[=(CAST(ITEM($0, >>>>> 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE >>>>> \"ISO-8859-1$en_US$primary\", 'CA')])\n" >>>>>> + + " ElasticsearchTableScan(table=[[elastic, zips]])"; >>>>>> + calciteAssert() >>>>>> + .query("select \"state\", \"city\" from zips where >>>>> \"state\" = 'CA'") >>>>>> + .limit(3) >>>>>> + .returnsUnordered("state=CA; city=BELL GARDENS", >>>>>> + "state=CA; city=LOS ANGELES", >>>>>> + "state=CA; city=NORWALK") >>>>>> + .explainContains(explain); >>>>>> + } >>>>>> + >>>>>> + @Test public void testFilterReversed() { >>>>>> + calciteAssert() >>>>>> + .query("select \"state\", \"city\" from zips where 'WI' < >>>>> \"state\" order by \"city\"") >>>>>> + .limit(2) >>>>>> + .returnsUnordered("state=WV; city=BECKLEY", >>>>>> + "state=WY; city=CHEYENNE"); >>>>>> + calciteAssert() >>>>>> + .query("select \"state\", \"city\" from zips where \"state\" >>>>>> 'WI' order by \"city\"") >>>>>> + .limit(2) >>>>>> + .returnsUnordered("state=WV; city=BECKLEY", >>>>>> + "state=WY; city=CHEYENNE"); >>>>>> + } >>>>>> + >>>>>> +} >>>>>> + >>>>>> +// End ElasticSearch2AdapterTest.java >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticNode.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticNode.java >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticNode.java >>>>>> new file mode 100644 >>>>>> index 0000000..4474add >>>>>> --- /dev/null >>>>>> +++ >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticNode.java >>>>>> @@ -0,0 +1,147 @@ >>>>>> +/* >>>>>> + * 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.elasticsearch2; >>>>>> + >>>>>> +import com.google.common.base.Preconditions; >>>>>> +import com.google.common.io.Files; >>>>>> + >>>>>> +import org.elasticsearch.Version; >>>>>> +import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; >>>>>> +import org.elasticsearch.action.admin.cluster.node.info >>>>> .NodesInfoResponse; >>>>>> +import org.elasticsearch.client.Client; >>>>>> +import org.elasticsearch.common.settings.Settings; >>>>>> +import org.elasticsearch.common.transport.TransportAddress; >>>>>> +import org.elasticsearch.node.Node; >>>>>> +import org.elasticsearch.node.internal.InternalSettingsPreparer; >>>>>> +import org.elasticsearch.plugins.Plugin; >>>>>> +import org.elasticsearch.script.groovy.GroovyPlugin; >>>>>> + >>>>>> +import java.io.File; >>>>>> +import java.util.Arrays; >>>>>> +import java.util.Collection; >>>>>> +import java.util.Collections; >>>>>> + >>>>>> +/** >>>>>> + * Represents a single elastic search node which can run embedded in a >>>>> java application. >>>>>> + * Intended for unit and integration tests. Settings and plugins are >>>>> crafted for Calcite. >>>>>> + */ >>>>>> +class EmbeddedElasticNode implements AutoCloseable { >>>>>> + >>>>>> + private final LocalNode node; >>>>>> + private volatile boolean isStarted; >>>>>> + >>>>>> + private EmbeddedElasticNode(LocalNode node) { >>>>>> + this.node = Preconditions.checkNotNull(node, "node"); >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Having separate class to expose (protected) constructor which >>>>> allows to install >>>>>> + * different plugins. In our case it is {@code GroovyPlugin} for >>>>> scripted fields like >>>>>> + * {@code loc[0]} or {@code loc[1]['foo']}. >>>>>> + */ >>>>>> + private static class LocalNode extends Node { >>>>>> + private LocalNode(Settings settings, Collection<Class<? extends >>>>> Plugin>> classpathPlugins) { >>>>>> + super(InternalSettingsPreparer.prepareEnvironment(settings, >>> null), >>>>>> + Version.CURRENT, >>>>>> + classpathPlugins); >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Creates an instance with existing settings >>>>>> + */ >>>>>> + private static EmbeddedElasticNode create(Settings settings) { >>>>>> + // ensure GroovyPlugin is installed or otherwise scripted fields >>>>> would not work >>>>>> + LocalNode node = new LocalNode(settings, >>>>> Collections.singleton(GroovyPlugin.class)); >>>>>> + return new EmbeddedElasticNode(node); >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Creates elastic node as single member of a cluster. Node will not >>>>> be started >>>>>> + * unless {@link #start()} is explicitly called. >>>>>> + */ >>>>>> + public static EmbeddedElasticNode create() { >>>>>> + File data = Files.createTempDir(); >>>>>> + data.deleteOnExit(); >>>>>> + File home = Files.createTempDir(); >>>>>> + home.deleteOnExit(); >>>>>> + >>>>>> + Settings settings = Settings.builder() >>>>>> + .put("node.name", "fake-elastic") >>>>>> + .put("path.home", home.getAbsolutePath()) >>>>>> + .put("path.data", data.getAbsolutePath()) >>>>>> + .put("script.inline", true) // requires GroovyPlugin >>>>>> + .put("script.indexed", true) // requires GroovyPlugin >>>>>> + .put("cluster.routing.allocation.disk.threshold_enabled", >>>>> false) >>>>>> + .put("node.local", true) >>>>>> + .put("node.data", true) >>>>>> + .put("network.host", "localhost") >>>>>> + .build(); >>>>>> + >>>>>> + return create(settings); >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Starts current node >>>>>> + */ >>>>>> + public void start() { >>>>>> + Preconditions.checkState(!isStarted, "already started"); >>>>>> + node.start(); >>>>>> + this.isStarted = true; >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Returns current address to connect to with HTTP client. >>>>>> + */ >>>>>> + public TransportAddress httpAddress() { >>>>>> + Preconditions.checkState(isStarted, "node is not started"); >>>>>> + >>>>>> + NodesInfoResponse response = >>>>> client().admin().cluster().prepareNodesInfo() >>>>>> + .execute().actionGet(); >>>>>> + if (response.getNodes().length != 1) { >>>>>> + throw new IllegalStateException("Expected single node but got " >>>>>> + + response.getNodes().length); >>>>>> + } >>>>>> + NodeInfo node = response.getNodes()[0]; >>>>>> + return node.getHttp().address().boundAddresses()[0]; >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Exposes elastic >>>>>> + * <a href=" >>>>> >>> https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/transport-client.html >>> ">transport >>>>> client</a> >>>>>> + * >>>>>> + * (use of HTTP client is preferred). >>>>>> + */ >>>>>> + public Client client() { >>>>>> + Preconditions.checkState(isStarted, "node is not started"); >>>>>> + return node.client(); >>>>>> + } >>>>>> + >>>>>> + @Override public void close() throws Exception { >>>>>> + node.close(); >>>>>> + // cleanup data dirs >>>>>> + File data = new File(node.settings().get("path.data")); >>>>>> + File home = new File(node.settings().get("path.home")); >>>>>> + for (File file: Arrays.asList(data, home)) { >>>>>> + if (file.exists()) { >>>>>> + file.delete(); >>>>>> + } >>>>>> + } >>>>>> + } >>>>>> +} >>>>>> + >>>>>> +// End EmbeddedElasticNode.java >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticRule.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticRule.java >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticRule.java >>>>>> new file mode 100644 >>>>>> index 0000000..a633078 >>>>>> --- /dev/null >>>>>> +++ >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticRule.java >>>>>> @@ -0,0 +1,97 @@ >>>>>> +/* >>>>>> + * 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.elasticsearch2; >>>>>> + >>>>>> +import com.google.common.base.Preconditions; >>>>>> + >>>>>> +import org.elasticsearch.client.Client; >>>>>> +import org.elasticsearch.common.transport.TransportAddress; >>>>>> +import org.junit.rules.ExternalResource; >>>>>> + >>>>>> +/** >>>>>> + * Used to initialize a single elastic node. For performance reasons >>>>> (node startup costs), >>>>>> + * same instance is usually shared across multiple tests. >>>>>> + * >>>>>> + * This rule should be used as follows: >>>>>> + * <pre> >>>>>> + * {@code >>>>>> + * >>>>>> + * public class MyTest { >>>>>> + * @literal @ClassRule >>>>>> + * public static final ElasticSearchRule RULE = >>>>> ElasticSearchRule.create(); >>>>>> + * >>>>>> + * @literal @BeforeClass >>>>>> + * public void setup() { >>>>>> + * // ... populate instance >>>>>> + * } >>>>>> + * >>>>>> + * @literal @Test >>>>>> + * public void myTest() { >>>>>> + * TransportAddress address = RULE.httpAddress(); >>>>>> + * // .... >>>>>> + * } >>>>>> + * } >>>>>> + * } >>>>>> + * </pre> >>>>>> + * >>>>>> + * @see ExternalResource >>>>>> + */ >>>>>> +class EmbeddedElasticRule extends ExternalResource { >>>>>> + >>>>>> + private final EmbeddedElasticNode node; >>>>>> + >>>>>> + private EmbeddedElasticRule(EmbeddedElasticNode resource) { >>>>>> + this.node = Preconditions.checkNotNull(resource, "resource"); >>>>>> + } >>>>>> + >>>>>> + @Override protected void before() throws Throwable { >>>>>> + node.start(); >>>>>> + } >>>>>> + >>>>>> + @Override protected void after() { >>>>>> + try { >>>>>> + node.close(); >>>>>> + } catch (Exception e) { >>>>>> + throw new RuntimeException(e); >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Factory method to create this rule. >>>>>> + */ >>>>>> + public static EmbeddedElasticRule create() { >>>>>> + return new EmbeddedElasticRule(EmbeddedElasticNode.create()); >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * Exposes current ES transport client. >>>>>> + */ >>>>>> + Client client() { >>>>>> + return node.client(); >>>>>> + } >>>>>> + >>>>>> + /** >>>>>> + * HTTP address for rest clients (can be ES native or any other). >>>>>> + */ >>>>>> + TransportAddress httpAddress() { >>>>>> + return node.httpAddress(); >>>>>> + } >>>>>> + >>>>>> + >>>>>> +} >>>>>> + >>>>>> +// End EmbeddedElasticRule.java >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticChecker.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticChecker.java >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticChecker.java >>>>>> new file mode 100644 >>>>>> index 0000000..21fc491 >>>>>> --- /dev/null >>>>>> +++ >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticChecker.java >>>>>> @@ -0,0 +1,49 @@ >>>>>> +/* >>>>>> + * 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.test; >>>>>> + >>>>>> +import com.google.common.base.Function; >>>>>> + >>>>>> +import java.util.List; >>>>>> + >>>>>> +import javax.annotation.Nullable; >>>>>> + >>>>>> +/** >>>>>> + * Internal util methods for ElasticSearch tests >>>>>> + */ >>>>>> +public class ElasticChecker { >>>>>> + >>>>>> + private ElasticChecker() {} >>>>>> + >>>>>> + >>>>>> + /** Returns a function that checks that a particular Elasticsearch >>>>> pipeline is >>>>>> + * generated to implement a query. */ >>>>>> + public static Function<List, Void> elasticsearchChecker(final >>>>> String... strings) { >>>>>> + return new Function<List, Void>() { >>>>>> + @Nullable >>>>>> + @Override public Void apply(@Nullable List actual) { >>>>>> + Object[] actualArray = actual == null || actual.isEmpty() ? >>> null >>>>>> + : ((List) actual.get(0)).toArray(); >>>>>> + CalciteAssert.assertArrayEqual("expected Elasticsearch query >>>>> not found", strings, >>>>>> + actualArray); >>>>>> + return null; >>>>>> + } >>>>>> + }; >>>>>> + } >>>>>> +} >>>>>> + >>>>>> +// End ElasticChecker.java >>>>>> >>>>>> >>>>> >>> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/test/java/org/apache/calcite/test/Elasticsearch2AdapterIT.java >>>>>> ---------------------------------------------------------------------- >>>>>> diff --git >>>>> >>> a/elasticsearch2/src/test/java/org/apache/calcite/test/Elasticsearch2AdapterIT.java >>>>> >>> b/elasticsearch2/src/test/java/org/apache/calcite/test/Elasticsearch2AdapterIT.java >>>>>> deleted file mode 100644 >>>>>> index 4e0c2b6..0000000 >>>>>> --- >>>>> >>> a/elasticsearch2/src/test/java/org/apache/calcite/test/Elasticsearch2AdapterIT.java >>>>>> +++ /dev/null >>>>>> @@ -1,270 +0,0 @@ >>>>>> -/* >>>>>> - * 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.test; >>>>>> - >>>>>> -import org.apache.calcite.util.Util; >>>>>> - >>>>>> -import com.google.common.base.Function; >>>>>> -import com.google.common.collect.ImmutableMap; >>>>>> - >>>>>> -import org.junit.Test; >>>>>> - >>>>>> -import java.util.List; >>>>>> -import javax.annotation.Nullable; >>>>>> - >>>>>> -/** >>>>>> - * Tests for the {@code org.apache.calcite.adapter.elasticsearch2} >>>>> package. >>>>>> - * >>>>>> - * <p>Before calling this test, you need to populate Elasticsearch, as >>>>> follows: >>>>>> - * >>>>>> - * <blockquote><code> >>>>>> - * git clone https://github.com/vlsi/calcite-test-dataset<br> >>>>>> - * cd calcite-test-dataset<br> >>>>>> - * mvn install >>>>>> - * </code></blockquote> >>>>>> - * >>>>>> - * <p>This will create a virtual machine with Elastic >
