git.net

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [2/2] calcite git commit: [CALCITE-2347] running ElasticSearch in embedded mode for unit tests of ES adapter (Andrei Sereda)


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, mmior@xxxxxxxxxx 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 <andrei@xxxxxxxxxx>
> Authored: Thu May 31 18:19:10 2018 -0400
> Committer: Michael Mior <mmior@xxxxxxxxxxxx>
> 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 Elasticsearch and the "zips" test
> - * dataset.
> - */
> -public class Elasticsearch2AdapterIT {
> -  /**
> -   * Whether to run Elasticsearch tests. Enabled by default, however test is only
> -   * included if "it" profile is activated ({@code -Pit}). To disable,
> -   * specify {@code -Dcalcite.test.elasticsearch=false} on the Java command line.
> -   */
> -  private static final boolean ENABLED = Util.getBooleanProperty("calcite.test.elasticsearch",
> -      true);
> -
> -  /** Connection factory based on the "zips-es" model. */
> -  private static final ImmutableMap<String, String> ZIPS = ImmutableMap.of("model",
> -      Elasticsearch2AdapterIT.class.getResource("/elasticsearch-zips-model.json").getPath());
> -
> -  /** Whether to run this test. */
> -  private boolean enabled() {
> -    return ENABLED;
> -  }
> -
> -  /** Returns a function that checks that a particular Elasticsearch pipeline is
> -   * generated to implement a query. */
> -  private 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;
> -      }
> -    };
> -  }
> -
> -  @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=[[elasticsearch_raw, zips]])";
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .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.that()
> -        .with(ZIPS)
> -        .query(sql)
> -        .returnsUnordered("state=AK; id=99503",
> -            "state=AK; id=99504",
> -            "state=AK; id=99505")
> -        .queryContains(
> -            elasticsearchChecker(
> -                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}",
> -                "\"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.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query(sql)
> -        .runs()
> -        .queryContains(
> -            elasticsearchChecker(
> -                "\"from\": 2",
> -                "\"size\": 3",
> -                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
> -  }
> -
> -  @Test public void testLimit() {
> -    final String sql = "select \"state\", \"id\" from zips\n"
> -        + "fetch next 3 rows only";
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query(sql)
> -        .runs()
> -        .queryContains(
> -            elasticsearchChecker(
> -                "\"size\": 3",
> -                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
> -  }
> -
> -  @Test public void testFilterSort() {
> -    final String sql = "select * from zips\n"
> -        + "where \"city\" = 'SPRINGFIELD' 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, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", 'SPRINGFIELD'), >=(CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", '70000'))])\n"
> -        + "        ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query(sql)
> -        .returnsOrdered(
> -            "city=SPRINGFIELD; longitude=-92.54567; latitude=35.274879; pop=752; state=AR; id=72157",
> -            "city=SPRINGFIELD; longitude=-102.617322; latitude=37.406727; pop=1992; state=CO; id=81073",
> -            "city=SPRINGFIELD; longitude=-90.577479; latitude=30.415738; pop=5597; state=LA; id=70462",
> -            "city=SPRINGFIELD; longitude=-123.015259; latitude=44.06106; pop=32384; state=OR; id=97477",
> -            "city=SPRINGFIELD; longitude=-122.917108; latitude=44.056056; pop=27521; state=OR; id=97478")
> -        .queryContains(
> -            elasticsearchChecker("\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
> -                    + "{\"must\":[{\"term\":{\"city\":\"springfield\"}},{\"range\":{\"id\":{\"gte\":\"70000\"}}}]}}}}",
> -                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}",
> -                "\"sort\": [ {\"state\": \"asc\"}, {\"id\": \"asc\"}]"))
> -        .explainContains(explain);
> -  }
> -
> -  @Test public void testFilterSortDesc() {
> -    final String sql = "select * from zips\n"
> -        + "where \"pop\" BETWEEN 20000 AND 20100\n"
> -        + "order by \"state\" desc, \"pop\"";
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query(sql)
> -        .limit(4)
> -        .returnsOrdered(
> -            "city=SHERIDAN; longitude=-106.964795; latitude=44.78486; pop=20025; state=WY; id=82801",
> -            "city=MOUNTLAKE TERRAC; longitude=-122.304036; latitude=47.793061; pop=20059; state=WA; id=98043",
> -            "city=FALMOUTH; longitude=-77.404537; latitude=38.314557; pop=20039; state=VA; id=22405",
> -            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104");
> -  }
> -
> -  @Test public void testFilterRedundant() {
> -    final String sql = "select * from zips\n"
> -        + "where \"state\" > 'CA' and \"state\" < 'AZ' and \"state\" = 'OK'";
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query(sql)
> -        .runs()
> -        .queryContains(
> -            elasticsearchChecker(""
> -                + "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
> -                + "{\"must\":[{\"term\":{\"state\":\"ok\"}}]}}}}",
> -                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"));
> -  }
> -
> -  @Test public void testInPlan() {
> -    final String[] searches = {
> -        "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":{\"should\":"
> -          + "[{\"bool\":{\"must\":[{\"term\":{\"pop\":20012}}]}},{\"bool\":{\"must\":[{\"term\":"
> -          + "{\"pop\":15590}}]}}]}}}}",
> -        "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"
> -    };
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query("select * from zips where \"pop\" in (20012, 15590)")
> -        .returnsUnordered(
> -            "city=COVINA; longitude=-117.884285; latitude=34.08596; pop=15590; state=CA; id=91723",
> -            "city=ARLINGTON; longitude=-97.091987; latitude=32.654752; pop=15590; state=TX; id=76018",
> -            "city=CROFTON; longitude=-76.680166; latitude=39.011163; pop=15590; state=MD; id=21114",
> -            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104",
> -            "city=DINUBA; longitude=-119.39087; latitude=36.534931; pop=20012; state=CA; id=93618")
> -        .queryContains(elasticsearchChecker(searches));
> -  }
> -
> -  @Test public void testZips() {
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .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.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query(sql)
> -        .limit(2)
> -        .returnsUnordered("state=AK; city=ELMENDORF AFB; zero=0",
> -            "state=AK; city=EIELSON AFB; zero=0")
> -        .queryContains(
> -            elasticsearchChecker("\"sort\": [ {\"state\": \"asc\"}, {\"city\": \"asc\"}]",
> -                "\"fields\" : [\"state\", \"city\"], \"script_fields\": {\"zero\":{\"script\": \"0\"}}"));
> -  }
> -
> -  @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=[[elasticsearch_raw, zips]])";
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query("select \"state\", \"city\" from zips where \"state\" = 'CA'")
> -        .limit(2)
> -        .returnsUnordered("state=CA; city=LOS ANGELES",
> -            "state=CA; city=LOS ANGELES")
> -        .explainContains(explain);
> -  }
> -
> -  @Test public void testFilterReversed() {
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query("select \"state\", \"city\" from zips where 'WI' < \"state\"")
> -        .limit(2)
> -        .returnsUnordered("state=WV; city=WELCH",
> -            "state=WV; city=HANOVER");
> -    CalciteAssert.that()
> -        .enable(enabled())
> -        .with(ZIPS)
> -        .query("select \"state\", \"city\" from zips where \"state\" > 'WI'")
> -        .limit(2)
> -        .returnsUnordered("state=WV; city=WELCH",
> -            "state=WV; city=HANOVER");
> -  }
> -}
> -
> -// End Elasticsearch2AdapterIT.java
> 
> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/test/resources/elasticsearch-zips-model.json
> ----------------------------------------------------------------------
> diff --git a/elasticsearch2/src/test/resources/elasticsearch-zips-model.json b/elasticsearch2/src/test/resources/elasticsearch-zips-model.json
> deleted file mode 100644
> index 6c00a9c..0000000
> --- a/elasticsearch2/src/test/resources/elasticsearch-zips-model.json
> +++ /dev/null
> @@ -1,50 +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.
> - */
> -{
> -  "version": "1.0",
> -  "defaultSchema": "elasticsearch",
> -  "schemas": [
> -    {
> -      "type": "custom",
> -      "name": "elasticsearch_raw",
> -      "factory": "org.apache.calcite.adapter.elasticsearch2.Elasticsearch2SchemaFactory",
> -      "operand": {
> -        "coordinates": "{'127.0.0.1': 9300}",
> -        "userConfig": "{'bulk.flush.max.actions': 10, 'bulk.flush.max.size.mb': 1}",
> -        "index": "usa"
> -      }
> -    },
> -    {
> -      "name": "elasticsearch",
> -      "tables": [
> -        {
> -          "name": "ZIPS",
> -          "type": "view",
> -          "sql": [
> -            "select cast(_MAP['city'] AS varchar(20)) AS \"city\",\n",
> -            " 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\",\n",
> -            " cast(_MAP['state'] AS varchar(2)) AS \"state\",\n",
> -            " cast(_MAP['id'] AS varchar(5)) AS \"id\"\n",
> -            "from \"elasticsearch_raw\".\"zips\""
> -          ]
> -        }
> -      ]
> -    }
> -  ]
> -}
> 
> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch2/src/test/resources/zips-mini.json
> ----------------------------------------------------------------------
> diff --git a/elasticsearch2/src/test/resources/zips-mini.json b/elasticsearch2/src/test/resources/zips-mini.json
> new file mode 100644
> index 0000000..d70eadc
> --- /dev/null
> +++ b/elasticsearch2/src/test/resources/zips-mini.json
> @@ -0,0 +1,149 @@
> +{ "_id" : "01701", "city" : "FRAMINGHAM", "loc" : [ -71.42548600000001, 42.300665 ], "pop" : 65046, "state" : "MA" }
> +{ "_id" : "02154", "city" : "NORTH WALTHAM", "loc" : [ -71.236497, 42.382492 ], "pop" : 57871, "state" : "MA" }
> +{ "_id" : "02401", "city" : "BROCKTON", "loc" : [ -71.03434799999999, 42.081571 ], "pop" : 59498, "state" : "MA" }
> +{ "_id" : "02840", "city" : "MIDDLETOWN", "loc" : [ -71.30347999999999, 41.504502 ], "pop" : 47687, "state" : "RI" }
> +{ "_id" : "02860", "city" : "PAWTUCKET", "loc" : [ -71.39071300000001, 41.872873 ], "pop" : 45442, "state" : "RI" }
> +{ "_id" : "02895", "city" : "NORTH SMITHFIELD", "loc" : [ -71.513683, 41.99948 ], "pop" : 53733, "state" : "RI" }
> +{ "_id" : "03060", "city" : "NASHUA", "loc" : [ -71.466684, 42.756395 ], "pop" : 41438, "state" : "NH" }
> +{ "_id" : "03103", "city" : "MANCHESTER", "loc" : [ -71.449325, 42.965563 ], "pop" : 36613, "state" : "NH" }
> +{ "_id" : "03301", "city" : "CONCORD", "loc" : [ -71.527734, 43.218525 ], "pop" : 34035, "state" : "NH" }
> +{ "_id" : "04240", "city" : "LEWISTON", "loc" : [ -70.191619, 44.098538 ], "pop" : 40173, "state" : "ME" }
> +{ "_id" : "04401", "city" : "BANGOR", "loc" : [ -68.791839, 44.824199 ], "pop" : 40434, "state" : "ME" }
> +{ "_id" : "05301", "city" : "BRATTLEBORO", "loc" : [ -72.593322, 42.857353 ], "pop" : 17522, "state" : "VT" }
> +{ "_id" : "05401", "city" : "BURLINGTON", "loc" : [ -73.219875, 44.484023 ], "pop" : 39127, "state" : "VT" }
> +{ "_id" : "05701", "city" : "RUTLAND", "loc" : [ -72.97077299999999, 43.614131 ], "pop" : 22576, "state" : "VT" }
> +{ "_id" : "06010", "city" : "BRISTOL", "loc" : [ -72.930193, 41.682293 ], "pop" : 60670, "state" : "CT" }
> +{ "_id" : "06450", "city" : "MERIDEN", "loc" : [ -72.799734, 41.533396 ], "pop" : 59441, "state" : "CT" }
> +{ "_id" : "06902", "city" : "STAMFORD", "loc" : [ -73.53742800000001, 41.052552 ], "pop" : 54605, "state" : "CT" }
> +{ "_id" : "07002", "city" : "BAYONNE", "loc" : [ -74.119169, 40.666399 ], "pop" : 61444, "state" : "NJ" }
> +{ "_id" : "07087", "city" : "WEEHAWKEN", "loc" : [ -74.030558, 40.768153 ], "pop" : 69646, "state" : "NJ" }
> +{ "_id" : "07111", "city" : "IRVINGTON", "loc" : [ -74.23127100000001, 40.7261 ], "pop" : 60986, "state" : "NJ" }
> +{ "_id" : "10021", "city" : "NEW YORK", "loc" : [ -73.958805, 40.768476 ], "pop" : 106564, "state" : "NY" }
> +{ "_id" : "11226", "city" : "BROOKLYN", "loc" : [ -73.956985, 40.646694 ], "pop" : 111396, "state" : "NY" }
> +{ "_id" : "11373", "city" : "JACKSON HEIGHTS", "loc" : [ -73.878551, 40.740388 ], "pop" : 88241, "state" : "NY" }
> +{ "_id" : "17042", "city" : "CLEONA", "loc" : [ -76.425895, 40.335912 ], "pop" : 61993, "state" : "PA" }
> +{ "_id" : "18042", "city" : "FORKS TOWNSHIP", "loc" : [ -75.23582, 40.6867 ], "pop" : 65784, "state" : "PA" }
> +{ "_id" : "19143", "city" : "PHILADELPHIA", "loc" : [ -75.228819, 39.944815 ], "pop" : 80454, "state" : "PA" }
> +{ "_id" : "19711", "city" : "NEWARK", "loc" : [ -75.737534, 39.701129 ], "pop" : 50573, "state" : "DE" }
> +{ "_id" : "19720", "city" : "MANOR", "loc" : [ -75.589938, 39.67703 ], "pop" : 46906, "state" : "DE" }
> +{ "_id" : "19901", "city" : "DOVER", "loc" : [ -75.535983, 39.156639 ], "pop" : 46005, "state" : "DE" }
> +{ "_id" : "20011", "city" : "WASHINGTON", "loc" : [ -77.020251, 38.951786 ], "pop" : 62924, "state" : "DC" }
> +{ "_id" : "20301", "city" : "PENTAGON", "loc" : [ -77.038196, 38.891019 ], "pop" : 21, "state" : "DC" }
> +{ "_id" : "21061", "city" : "GLEN BURNIE", "loc" : [ -76.61886199999999, 39.158968 ], "pop" : 75692, "state" : "MD" }
> +{ "_id" : "21207", "city" : "GWYNN OAK", "loc" : [ -76.734064, 39.329628 ], "pop" : 76002, "state" : "MD" }
> +{ "_id" : "21215", "city" : "BALTIMORE", "loc" : [ -76.67939699999999, 39.344572 ], "pop" : 74402, "state" : "MD" }
> +{ "_id" : "22901", "city" : "CHARLOTTESVILLE", "loc" : [ -78.490869, 38.054752 ], "pop" : 62708, "state" : "VA" }
> +{ "_id" : "23464", "city" : "VIRGINIA BEACH", "loc" : [ -76.175909, 36.797772 ], "pop" : 67276, "state" : "VA" }
> +{ "_id" : "23602", "city" : "NEWPORT NEWS", "loc" : [ -76.53212499999999, 37.131684 ], "pop" : 68525, "state" : "VA" }
> +{ "_id" : "25801", "city" : "BECKLEY", "loc" : [ -81.206084, 37.793214 ], "pop" : 45196, "state" : "WV" }
> +{ "_id" : "26003", "city" : "ELM GROVE", "loc" : [ -80.685126, 40.072736 ], "pop" : 49136, "state" : "WV" }
> +{ "_id" : "26505", "city" : "STAR CITY", "loc" : [ -79.95422499999999, 39.633858 ], "pop" : 70185, "state" : "WV" }
> +{ "_id" : "27292", "city" : "LEXINGTON", "loc" : [ -80.262049, 35.82306 ], "pop" : 69179, "state" : "NC" }
> +{ "_id" : "28677", "city" : "STATESVILLE", "loc" : [ -80.894009, 35.799022 ], "pop" : 52895, "state" : "NC" }
> +{ "_id" : "29150", "city" : "OSWEGO", "loc" : [ -80.32100800000001, 33.928199 ], "pop" : 46394, "state" : "SC" }
> +{ "_id" : "29501", "city" : "FLORENCE", "loc" : [ -79.772786, 34.18375 ], "pop" : 66990, "state" : "SC" }
> +{ "_id" : "29801", "city" : "AIKEN", "loc" : [ -81.71942900000001, 33.553024 ], "pop" : 51233, "state" : "SC" }
> +{ "_id" : "30032", "city" : "DECATUR", "loc" : [ -84.263165, 33.740825 ], "pop" : 56056, "state" : "GA" }
> +{ "_id" : "30906", "city" : "PEACH ORCHARD", "loc" : [ -82.038358, 33.402024 ], "pop" : 58646, "state" : "GA" }
> +{ "_id" : "32216", "city" : "JACKSONVILLE", "loc" : [ -81.547387, 30.293907 ], "pop" : 58867, "state" : "FL" }
> +{ "_id" : "33012", "city" : "HIALEAH", "loc" : [ -80.30589999999999, 25.865395 ], "pop" : 73194, "state" : "FL" }
> +{ "_id" : "33311", "city" : "FORT LAUDERDALE", "loc" : [ -80.172786, 26.142104 ], "pop" : 65378, "state" : "FL" }
> +{ "_id" : "35215", "city" : "CENTER POINT", "loc" : [ -86.693197, 33.635447 ], "pop" : 43862, "state" : "AL" }
> +{ "_id" : "35401", "city" : "TUSCALOOSA", "loc" : [ -87.56266599999999, 33.196891 ], "pop" : 42124, "state" : "AL" }
> +{ "_id" : "35901", "city" : "SOUTHSIDE", "loc" : [ -86.010279, 33.997248 ], "pop" : 44165, "state" : "AL" }
> +{ "_id" : "37042", "city" : "CLARKSVILLE", "loc" : [ -87.418621, 36.585315 ], "pop" : 43296, "state" : "TN" }
> +{ "_id" : "37211", "city" : "NASHVILLE", "loc" : [ -86.72403799999999, 36.072486 ], "pop" : 51478, "state" : "TN" }
> +{ "_id" : "38109", "city" : "MEMPHIS", "loc" : [ -90.073238, 35.042538 ], "pop" : 60508, "state" : "TN" }
> +{ "_id" : "39180", "city" : "VICKSBURG", "loc" : [ -90.85065, 32.325824 ], "pop" : 46968, "state" : "MS" }
> +{ "_id" : "39401", "city" : "HATTIESBURG", "loc" : [ -89.306471, 31.314553 ], "pop" : 41866, "state" : "MS" }
> +{ "_id" : "39440", "city" : "LAUREL", "loc" : [ -89.13115500000001, 31.705444 ], "pop" : 45040, "state" : "MS" }
> +{ "_id" : "40214", "city" : "LOUISVILLE", "loc" : [ -85.77802699999999, 38.159318 ], "pop" : 42198, "state" : "KY" }
> +{ "_id" : "40216", "city" : "SHIVELY", "loc" : [ -85.831771, 38.186138 ], "pop" : 41719, "state" : "KY" }
> +{ "_id" : "40601", "city" : "HATTON", "loc" : [ -84.88061, 38.192831 ], "pop" : 46563, "state" : "KY" }
> +{ "_id" : "44035", "city" : "ELYRIA", "loc" : [ -82.10508799999999, 41.372353 ], "pop" : 66674, "state" : "OH" }
> +{ "_id" : "44060", "city" : "MENTOR", "loc" : [ -81.342133, 41.689468 ], "pop" : 60109, "state" : "OH" }
> +{ "_id" : "44107", "city" : "EDGEWATER", "loc" : [ -81.79714300000001, 41.482654 ], "pop" : 59702, "state" : "OH" }
> +{ "_id" : "46360", "city" : "MICHIGAN CITY", "loc" : [ -86.869899, 41.698031 ], "pop" : 55392, "state" : "IN" }
> +{ "_id" : "47130", "city" : "JEFFERSONVILLE", "loc" : [ -85.735885, 38.307767 ], "pop" : 56543, "state" : "IN" }
> +{ "_id" : "47906", "city" : "WEST LAFAYETTE", "loc" : [ -86.923661, 40.444025 ], "pop" : 54702, "state" : "IN" }
> +{ "_id" : "48180", "city" : "TAYLOR", "loc" : [ -83.267269, 42.231738 ], "pop" : 70811, "state" : "MI" }
> +{ "_id" : "48185", "city" : "WESTLAND", "loc" : [ -83.374908, 42.318882 ], "pop" : 84712, "state" : "MI" }
> +{ "_id" : "48227", "city" : "DETROIT", "loc" : [ -83.193732, 42.388303 ], "pop" : 68390, "state" : "MI" }
> +{ "_id" : "50010", "city" : "AMES", "loc" : [ -93.639398, 42.029859 ], "pop" : 52105, "state" : "IA" }
> +{ "_id" : "50317", "city" : "PLEASANT HILL", "loc" : [ -93.549446, 41.612499 ], "pop" : 39883, "state" : "IA" }
> +{ "_id" : "52001", "city" : "DUBUQUE", "loc" : [ -90.68191400000001, 42.514977 ], "pop" : 41934, "state" : "IA" }
> +{ "_id" : "53209", "city" : "MILWAUKEE", "loc" : [ -87.947834, 43.118765 ], "pop" : 51008, "state" : "WI" }
> +{ "_id" : "54401", "city" : "WAUSAU", "loc" : [ -89.633955, 44.963433 ], "pop" : 51083, "state" : "WI" }
> +{ "_id" : "54901", "city" : "OSHKOSH", "loc" : [ -88.54363499999999, 44.021962 ], "pop" : 57187, "state" : "WI" }
> +{ "_id" : "55106", "city" : "SAINT PAUL", "loc" : [ -93.048817, 44.968384 ], "pop" : 47905, "state" : "MN" }
> +{ "_id" : "55112", "city" : "NEW BRIGHTON", "loc" : [ -93.199691, 45.074129 ], "pop" : 44128, "state" : "MN" }
> +{ "_id" : "55337", "city" : "BURNSVILLE", "loc" : [ -93.275283, 44.76086 ], "pop" : 51421, "state" : "MN" }
> +{ "_id" : "57103", "city" : "SIOUX FALLS", "loc" : [ -96.686415, 43.537386 ], "pop" : 32508, "state" : "SD" }
> +{ "_id" : "57401", "city" : "ABERDEEN", "loc" : [ -98.485642, 45.466109 ], "pop" : 28786, "state" : "SD" }
> +{ "_id" : "57701", "city" : "ROCKERVILLE", "loc" : [ -103.200259, 44.077041 ], "pop" : 45328, "state" : "SD" }
> +{ "_id" : "58103", "city" : "FARGO", "loc" : [ -96.812252, 46.856406 ], "pop" : 38483, "state" : "ND" }
> +{ "_id" : "58501", "city" : "BISMARCK", "loc" : [ -100.774755, 46.823448 ], "pop" : 36602, "state" : "ND" }
> +{ "_id" : "58701", "city" : "MINOT", "loc" : [ -101.298476, 48.22914 ], "pop" : 42195, "state" : "ND" }
> +{ "_id" : "59102", "city" : "BILLINGS", "loc" : [ -108.572662, 45.781265 ], "pop" : 40121, "state" : "MT" }
> +{ "_id" : "59601", "city" : "HELENA", "loc" : [ -112.021283, 46.613066 ], "pop" : 40102, "state" : "MT" }
> +{ "_id" : "59801", "city" : "MISSOULA", "loc" : [ -114.025207, 46.856274 ], "pop" : 33811, "state" : "MT" }
> +{ "_id" : "60623", "city" : "CHICAGO", "loc" : [ -87.7157, 41.849015 ], "pop" : 112047, "state" : "IL" }
> +{ "_id" : "60634", "city" : "NORRIDGE", "loc" : [ -87.796054, 41.945213 ], "pop" : 69160, "state" : "IL" }
> +{ "_id" : "60650", "city" : "CICERO", "loc" : [ -87.76008, 41.84776 ], "pop" : 67670, "state" : "IL" }
> +{ "_id" : "63031", "city" : "FLORISSANT", "loc" : [ -90.340097, 38.806865 ], "pop" : 52659, "state" : "MO" }
> +{ "_id" : "63116", "city" : "SAINT LOUIS", "loc" : [ -90.26254299999999, 38.581356 ], "pop" : 49014, "state" : "MO" }
> +{ "_id" : "63136", "city" : "JENNINGS", "loc" : [ -90.260189, 38.738878 ], "pop" : 54994, "state" : "MO" }
> +{ "_id" : "66502", "city" : "MANHATTAN", "loc" : [ -96.585776, 39.193757 ], "pop" : 50178, "state" : "KS" }
> +{ "_id" : "67212", "city" : "WICHITA", "loc" : [ -97.438344, 37.700683 ], "pop" : 41349, "state" : "KS" }
> +{ "_id" : "67401", "city" : "BAVARIA", "loc" : [ -97.60878700000001, 38.823802 ], "pop" : 45208, "state" : "KS" }
> +{ "_id" : "68104", "city" : "OMAHA", "loc" : [ -95.999888, 41.29186 ], "pop" : 35325, "state" : "NE" }
> +{ "_id" : "68502", "city" : "LINCOLN", "loc" : [ -96.693763, 40.789282 ], "pop" : 27576, "state" : "NE" }
> +{ "_id" : "68847", "city" : "KEARNEY", "loc" : [ -99.077883, 40.713608 ], "pop" : 28674, "state" : "NE" }
> +{ "_id" : "70072", "city" : "MARRERO", "loc" : [ -90.110462, 29.859756 ], "pop" : 58905, "state" : "LA" }
> +{ "_id" : "70117", "city" : "NEW ORLEANS", "loc" : [ -90.03124, 29.970298 ], "pop" : 56494, "state" : "LA" }
> +{ "_id" : "70560", "city" : "NEW IBERIA", "loc" : [ -91.819959, 30.001027 ], "pop" : 56105, "state" : "LA" }
> +{ "_id" : "72032", "city" : "CONWAY", "loc" : [ -92.423574, 35.084199 ], "pop" : 43236, "state" : "AR" }
> +{ "_id" : "72076", "city" : "GRAVEL RIDGE", "loc" : [ -92.13043500000001, 34.881985 ], "pop" : 37428, "state" : "AR" }
> +{ "_id" : "72401", "city" : "JONESBORO", "loc" : [ -90.69652600000001, 35.833016 ], "pop" : 53532, "state" : "AR" }
> +{ "_id" : "73034", "city" : "EDMOND", "loc" : [ -97.47983499999999, 35.666483 ], "pop" : 43814, "state" : "OK" }
> +{ "_id" : "73505", "city" : "LAWTON", "loc" : [ -98.455234, 34.617939 ], "pop" : 45542, "state" : "OK" }
> +{ "_id" : "74801", "city" : "SHAWNEE", "loc" : [ -96.931321, 35.34907 ], "pop" : 40076, "state" : "OK" }
> +{ "_id" : "78207", "city" : "SAN ANTONIO", "loc" : [ -98.52596699999999, 29.422855 ], "pop" : 58355, "state" : "TX" }
> +{ "_id" : "78521", "city" : "BROWNSVILLE", "loc" : [ -97.461236, 25.922103 ], "pop" : 79463, "state" : "TX" }
> +{ "_id" : "78572", "city" : "ALTON", "loc" : [ -98.342647, 26.24153 ], "pop" : 67604, "state" : "TX" }
> +{ "_id" : "80123", "city" : "BOW MAR", "loc" : [ -105.07766, 39.596854 ], "pop" : 59418, "state" : "CO" }
> +{ "_id" : "80221", "city" : "FEDERAL HEIGHTS", "loc" : [ -105.007985, 39.840562 ], "pop" : 54069, "state" : "CO" }
> +{ "_id" : "80631", "city" : "GARDEN CITY", "loc" : [ -104.704756, 40.413968 ], "pop" : 53905, "state" : "CO" }
> +{ "_id" : "82001", "city" : "CHEYENNE", "loc" : [ -104.796234, 41.143719 ], "pop" : 33107, "state" : "WY" }
> +{ "_id" : "82070", "city" : "LARAMIE", "loc" : [ -105.581146, 41.312907 ], "pop" : 29327, "state" : "WY" }
> +{ "_id" : "82716", "city" : "GILLETTE", "loc" : [ -105.497442, 44.282009 ], "pop" : 25968, "state" : "WY" }
> +{ "_id" : "83301", "city" : "TWIN FALLS", "loc" : [ -114.469265, 42.556495 ], "pop" : 34539, "state" : "ID" }
> +{ "_id" : "83704", "city" : "BOISE", "loc" : [ -116.295099, 43.633001 ], "pop" : 40912, "state" : "ID" }
> +{ "_id" : "83814", "city" : "COEUR D ALENE", "loc" : [ -116.784976, 47.692841 ], "pop" : 33589, "state" : "ID" }
> +{ "_id" : "84118", "city" : "KEARNS", "loc" : [ -111.98521, 40.652759 ], "pop" : 55999, "state" : "UT" }
> +{ "_id" : "84120", "city" : "WEST VALLEY CITY", "loc" : [ -112.009783, 40.68708 ], "pop" : 52854, "state" : "UT" }
> +{ "_id" : "84604", "city" : "PROVO", "loc" : [ -111.654906, 40.260681 ], "pop" : 43841, "state" : "UT" }
> +{ "_id" : "85023", "city" : "PHOENIX", "loc" : [ -112.111838, 33.632383 ], "pop" : 54668, "state" : "AZ" }
> +{ "_id" : "85204", "city" : "MESA", "loc" : [ -111.789554, 33.399168 ], "pop" : 55180, "state" : "AZ" }
> +{ "_id" : "85364", "city" : "YUMA", "loc" : [ -114.642362, 32.701507 ], "pop" : 57131, "state" : "AZ" }
> +{ "_id" : "87501", "city" : "POJOAQUE VALLEY", "loc" : [ -105.974818, 35.702472 ], "pop" : 51715, "state" : "NM" }
> +{ "_id" : "88001", "city" : "LAS CRUCES", "loc" : [ -106.746034, 32.321641 ], "pop" : 57502, "state" : "NM" }
> +{ "_id" : "88201", "city" : "ROSWELL", "loc" : [ -104.525857, 33.388504 ], "pop" : 53644, "state" : "NM" }
> +{ "_id" : "89031", "city" : "NORTH LAS VEGAS", "loc" : [ -115.124832, 36.206228 ], "pop" : 48113, "state" : "NV" }
> +{ "_id" : "89115", "city" : "LAS VEGAS", "loc" : [ -115.067062, 36.215818 ], "pop" : 51532, "state" : "NV" }
> +{ "_id" : "89502", "city" : "RENO", "loc" : [ -119.776395, 39.497239 ], "pop" : 38332, "state" : "NV" }
> +{ "_id" : "90011", "city" : "LOS ANGELES", "loc" : [ -118.258189, 34.007856 ], "pop" : 96074, "state" : "CA" }
> +{ "_id" : "90201", "city" : "BELL GARDENS", "loc" : [ -118.17205, 33.969177 ], "pop" : 99568, "state" : "CA" }
> +{ "_id" : "90650", "city" : "NORWALK", "loc" : [ -118.081767, 33.90564 ], "pop" : 94188, "state" : "CA" }
> +{ "_id" : "96734", "city" : "KAILUA", "loc" : [ -157.744781, 21.406262 ], "pop" : 53403, "state" : "HI" }
> +{ "_id" : "96744", "city" : "KANEOHE", "loc" : [ -157.811543, 21.422819 ], "pop" : 55236, "state" : "HI" }
> +{ "_id" : "96818", "city" : "HONOLULU", "loc" : [ -157.926925, 21.353173 ], "pop" : 62915, "state" : "HI" }
> +{ "_id" : "97005", "city" : "BEAVERTON", "loc" : [ -122.805395, 45.475035 ], "pop" : 46660, "state" : "OR" }
> +{ "_id" : "97206", "city" : "PORTLAND", "loc" : [ -122.59727, 45.483995 ], "pop" : 43134, "state" : "OR" }
> +{ "_id" : "97301", "city" : "SALEM", "loc" : [ -122.979692, 44.926039 ], "pop" : 48007, "state" : "OR" }
> +{ "_id" : "98031", "city" : "KENT", "loc" : [ -122.193184, 47.388004 ], "pop" : 50515, "state" : "WA" }
> +{ "_id" : "98059", "city" : "RENTON", "loc" : [ -122.151178, 47.467383 ], "pop" : 48197, "state" : "WA" }
> +{ "_id" : "98310", "city" : "BREMERTON", "loc" : [ -122.629913, 47.601916 ], "pop" : 49057, "state" : "WA" }
> +{ "_id" : "99504", "city" : "ANCHORAGE", "loc" : [ -149.74467, 61.203696 ], "pop" : 32383, "state" : "AK" }
> +{ "_id" : "99709", "city" : "FAIRBANKS", "loc" : [ -147.846917, 64.85437 ], "pop" : 23238, "state" : "AK" }
> +{ "_id" : "99801", "city" : "JUNEAU", "loc" : [ -134.529429, 58.362767 ], "pop" : 24947, "state" : "AK" }
> \ No newline at end of file
> 
> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch5/pom.xml
> ----------------------------------------------------------------------
> diff --git a/elasticsearch5/pom.xml b/elasticsearch5/pom.xml
> index 21d60f1..0fe9df9 100644
> --- a/elasticsearch5/pom.xml
> +++ b/elasticsearch5/pom.xml
> @@ -78,6 +78,35 @@ limitations under the License.
>       <version>${elasticsearch5-java-driver.version}</version>
>     </dependency>
>     <dependency>
> +      <groupId>org.elasticsearch.plugin</groupId>
> +      <artifactId>transport-netty3-client</artifactId>
> +      <version>${elasticsearch5-java-driver.version}</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +      <!-- Elastic search doesn't export painless script artifact to maven central.
> +      Using 3rd party version (codelibs)
> +      This JAR is used only in tests
> +      https://discuss.elastic.co/t/5-0-0-using-painless-in-esintegtestcase/64447/13
> +      -->
> +      <groupId>org.codelibs.elasticsearch.module</groupId>
> +      <artifactId>lang-painless</artifactId>
> +      <version>${elasticsearch5-java-driver.version}</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.logging.log4j</groupId>
> +      <artifactId>log4j-api</artifactId>
> +      <version>${log4j2.version}</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.logging.log4j</groupId>
> +      <artifactId>log4j-core</artifactId>
> +      <version>${log4j2.version}</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
>       <groupId>com.carrotsearch</groupId>
>       <artifactId>hppc</artifactId>
>       <version>${hppc.version}</version>
> @@ -110,6 +139,8 @@ limitations under the License.
>                 <ignoredUnusedDeclaredDependency>org.apache.calcite.avatica:avatica</ignoredUnusedDeclaredDependency>
>                 <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-api</ignoredUnusedDeclaredDependency>
>                 <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
> +                <ignoredUnusedDeclaredDependency>org.apache.logging.log4j:log4j-api</ignoredUnusedDeclaredDependency>
> +                <ignoredUnusedDeclaredDependency>org.apache.logging.log4j:log4j-core</ignoredUnusedDeclaredDependency>
>               </ignoredUnusedDeclaredDependencies>
>             </configuration>
>           </execution>
> 
> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Schema.java
> ----------------------------------------------------------------------
> diff --git a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Schema.java b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Schema.java
> index 6e16f2f..ebb96c8 100644
> --- a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Schema.java
> +++ b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Schema.java
> @@ -22,6 +22,8 @@ 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;
> 
> @@ -87,6 +89,17 @@ public class Elasticsearch5Schema extends AbstractSchema
>     }
>   }
> 
> +  /**
> +   * Allows schema to be instantiated from existing elastic search client.
> +   * This constructor is used in tests.
> +   */
> +  @VisibleForTesting
> +  Elasticsearch5Schema(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();
> 
> @@ -123,8 +136,10 @@ public class Elasticsearch5Schema 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 from "
> +              + transportNodes);
>     }
> 
>     client = transportClient;
> 
> http://git-wip-us.apache.org/repos/asf/calcite/blob/c12cb4b0/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Table.java
> ----------------------------------------------------------------------
> diff --git a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Table.java b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Table.java
> index 175e3cc..66e3edf 100644
> --- a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Table.java
> +++ b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Table.java
> @@ -25,15 +25,19 @@ import org.apache.calcite.linq4j.function.Function1;
> import org.apache.calcite.util.Util;
> 
> import org.elasticsearch.client.Client;
> +import org.elasticsearch.common.settings.Settings;
> import org.elasticsearch.common.xcontent.NamedXContentRegistry;
> import org.elasticsearch.common.xcontent.XContent;
> import org.elasticsearch.common.xcontent.XContentParser;
> import org.elasticsearch.common.xcontent.json.JsonXContent;
> import org.elasticsearch.index.query.QueryParseContext;
> import org.elasticsearch.search.SearchHit;
> +import org.elasticsearch.search.SearchModule;
> import org.elasticsearch.search.builder.SearchSourceBuilder;
> 
> import java.io.IOException;
> +import java.io.UncheckedIOException;
> +import java.util.Collections;
> import java.util.Iterator;
> import java.util.List;
> import java.util.Map;
> @@ -61,13 +65,16 @@ public class Elasticsearch5Table extends AbstractElasticsearchTable {
>       searchSourceBuilder = new SearchSourceBuilder();
>     } else {
>       String queryString = "{" + Util.toString(ops, "", ", ", "") + "}";
> -      NamedXContentRegistry xContentRegistry = NamedXContentRegistry.EMPTY;
>       XContent xContent = JsonXContent.jsonXContent;
> +      SearchModule searchModule = new SearchModule(Settings.EMPTY, false, Collections.emptyList());
> +      NamedXContentRegistry xContentRegistry =
> +              new NamedXContentRegistry(searchModule.getNamedXContents());
> +
>       try (XContentParser parser = xContent.createParser(xContentRegistry, queryString)) {
>         final QueryParseContext queryParseContext = new QueryParseContext(parser);
>         searchSourceBuilder = SearchSourceBuilder.fromXContent(queryParseContext);
>       } catch (IOException ex) {
> -        throw new RuntimeException(ex);
> +        throw new UncheckedIOException(ex);
>       }
>     }
>     final Function1<SearchHit, Object> getter = Elasticsearch5Enumerator.getter(fields);
> 





( ! ) Warning: include(msgfooter.php): failed to open stream: No such file or directory in /var/www/git/apache-calcite-development/msg03830.html on line 1759
Call Stack
#TimeMemoryFunctionLocation
10.0020483544{main}( ).../msg03830.html:0

( ! ) Warning: include(): Failed opening 'msgfooter.php' for inclusion (include_path='.:/var/www/git') in /var/www/git/apache-calcite-development/msg03830.html on line 1759
Call Stack
#TimeMemoryFunctionLocation
10.0020483544{main}( ).../msg03830.html:0