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

Reply via email to