You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mm...@apache.org on 2018/07/09 16:15:12 UTC

[1/5] calcite git commit: [CALCITE-2376] Unify ES2 and ES5 adapters. Migrate to low-level ES rest client as main transport. (Andrei Sereda)

Repository: calcite
Updated Branches:
  refs/heads/master 195414d70 -> 988381807


http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5AdapterTest.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5AdapterTest.java b/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5AdapterTest.java
deleted file mode 100644
index 9dedbda..0000000
--- a/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5AdapterTest.java
+++ /dev/null
@@ -1,405 +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.adapter.elasticsearch5;
-
-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.ElasticsearchChecker;
-
-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.action.support.WriteRequest;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.common.xcontent.XContentType;
-
-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 the Elasticsearch 5 adapter.
- *
- * <p>Uses a real instance via {@link EmbeddedElasticsearchPolicy}.
- * The document source is a local {@code zips-mini.json} file
- * (located in the classpath).
- */
-public class Elasticsearch5AdapterTest {
-
-  @ClassRule // init once for all tests
-  public static final EmbeddedElasticsearchPolicy POLICY =
-      EmbeddedElasticsearchPolicy.create();
-
-  private static final String ZIPS = "zips";
-
-  /**
-   * Used to create {@code zips} index and insert some data
-   *
-   * @throws Exception when ES instance setup failed
-   */
-  @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
-    POLICY.client().admin().indices()
-        .prepareCreate(ZIPS)
-        .addMapping(ZIPS, mapping)
-        .get();
-
-    BulkRequestBuilder bulk = POLICY.client().prepareBulk()
-        .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
-
-    // load records from file
-    Resources.readLines(Elasticsearch5AdapterTest.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(POLICY.client().prepareIndex(ZIPS, ZIPS)
-                .setSource(line.getBytes(StandardCharsets.UTF_8), XContentType.JSON));
-            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", POLICY.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 Elasticsearch5Schema(POLICY.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() {
-    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\", \"pop\" from zips\n"
-        + "order by \"state\", \"pop\" offset 2 rows fetch next 3 rows only";
-    calciteAssert()
-        .query(sql)
-        .returnsUnordered("state=AK; pop=32383",
-            "state=AL; pop=42124",
-            "state=AL; pop=43862")
-        .queryContains(
-            ElasticsearchChecker.elasticsearchChecker(
-                "\"_source\" : [\"state\", \"pop\"]",
-                "\"sort\": [ {\"state\": \"asc\"}, {\"pop\": \"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(
-            ElasticsearchChecker.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(
-            ElasticsearchChecker.elasticsearchChecker(
-                "\"size\": 3",
-                "\"_source\" : [\"state\", \"id\"]"));
-  }
-
-  @Test public void testFilterSort() {
-    final String sql = "select * from zips\n"
-        + "where \"state\" = 'CA' and \"pop\" >= 94000\n"
-        + "order by \"state\", \"pop\"";
-    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
-        + "  ElasticsearchSort(sort0=[$4], sort1=[$3], 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, 'pop')):INTEGER, 94000))])\n"
-        + "        ElasticsearchTableScan(table=[[elastic, zips]])\n\n";
-    calciteAssert()
-        .query(sql)
-        .returnsOrdered("city=NORWALK; longitude=-118.081767; latitude=33.90564;"
-                + " pop=94188; state=CA; id=90650",
-            "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")
-        .queryContains(
-            ElasticsearchChecker.elasticsearchChecker("\"query\" : "
-                    + "{\"constant_score\":{\"filter\":{\"bool\":"
-                    + "{\"must\":[{\"term\":{\"state\":\"CA\"}},"
-                    + "{\"range\":{\"pop\":{\"gte\":94000}}}]}}}}",
-                "\"script_fields\": {\"longitude\":{\"script\":\"params._source.loc[0]\"}, "
-                    + "\"latitude\":{\"script\":\"params._source.loc[1]\"}, "
-                    + "\"city\":{\"script\": \"params._source.city\"}, "
-                    + "\"pop\":{\"script\": \"params._source.pop\"}, "
-                    + "\"state\":{\"script\": \"params._source.state\"}, "
-                    + "\"id\":{\"script\": \"params._source.id\"}}",
-                "\"sort\": [ {\"state\": \"asc\"}, {\"pop\": \"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(
-            ElasticsearchChecker.elasticsearchChecker(""
-                    + "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
-                    + "{\"must\":[{\"term\":{\"state\":\"OK\"}}]}}}}",
-                "\"script_fields\": {\"longitude\":{\"script\":\"params._source.loc[0]\"}, "
-                    +  "\"latitude\":{\"script\":\"params._source.loc[1]\"}, "
-                    +   "\"city\":{\"script\": \"params._source.city\"}, "
-                    +   "\"pop\":{\"script\": \"params._source.pop\"}, \"state\":{\"script\": \"params._source.state\"}, "
-                    +            "\"id\":{\"script\": \"params._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\":\"params._source.loc[0]\"}, "
-            +  "\"latitude\":{\"script\":\"params._source.loc[1]\"}, "
-            +  "\"city\":{\"script\": \"params._source.city\"}, "
-            +  "\"pop\":{\"script\": \"params._source.pop\"}, "
-            +  "\"state\":{\"script\": \"params._source.state\"}, "
-            +  "\"id\":{\"script\": \"params._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(ElasticsearchChecker.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(
-            ElasticsearchChecker.elasticsearchChecker("\"script_fields\": "
-                    + "{\"zero\":{\"script\": \"0\"}, "
-                    + "\"state\":{\"script\": \"params._source.state\"}, "
-                    + "\"city\":{\"script\": \"params._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 Elasticsearch5AdapterTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchNode.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchNode.java b/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchNode.java
deleted file mode 100644
index a28b6e0..0000000
--- a/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchNode.java
+++ /dev/null
@@ -1,162 +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.adapter.elasticsearch5;
-
-import com.google.common.base.Preconditions;
-import com.google.common.io.Files;
-
-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.InternalSettingsPreparer;
-import org.elasticsearch.node.Node;
-import org.elasticsearch.node.NodeValidationException;
-import org.elasticsearch.painless.PainlessPlugin;
-import org.elasticsearch.plugins.Plugin;
-import org.elasticsearch.transport.Netty3Plugin;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Objects;
-
-/**
- * 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 EmbeddedElasticsearchNode implements AutoCloseable {
-
-  private final Node node;
-  private volatile boolean  isStarted;
-
-  private EmbeddedElasticsearchNode(Node node) {
-    this.node = Objects.requireNonNull(node, "node");
-  }
-
-
-  /**
-   * Creates an instance with existing settings
-   *
-   * @param settings ES configuration
-   * @return un-initialized node. Use {@link #start()} explicitly.
-   */
-  private static EmbeddedElasticsearchNode create(Settings settings) {
-    // ensure GroovyPlugin is installed or otherwise scripted fields would not work
-    Node node = new LocalNode(settings, Arrays.asList(Netty3Plugin.class, PainlessPlugin.class));
-    return new EmbeddedElasticsearchNode(node);
-  }
-
-  /**
-   * Creates elastic node as single member of a cluster. Node will not be started
-   * unless {@link #start()} is explicitly called.
-   *
-   * @return un-initialized node. Use {@link #start()} explicitly.
-   */
-  public static EmbeddedElasticsearchNode 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("transport.type", "local")
-            .put("http.type", "netty3")
-            .put("script.inline", true)  // requires groovy or painless
-            .put("network.host", "localhost")
-            .build();
-
-    return create(settings);
-  }
-
-  /**
-   * Starts current node
-   */
-  public void start() {
-    Preconditions.checkState(!isStarted, "already started");
-    try {
-      node.start();
-      this.isStarted = true;
-    } catch (NodeValidationException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
-  /**
-   * Returns the current address to connect to with HTTP client.
-   *
-   * @return {@code HTTP} protocol connection parameters
-   */
-  TransportAddress httpAddress() {
-    Preconditions.checkState(isStarted, "node is not started");
-
-    NodesInfoResponse response =  client().admin().cluster().prepareNodesInfo()
-            .execute().actionGet();
-    if (response.getNodes().size() != 1) {
-      throw new IllegalStateException("Expected single node but got "
-              + response.getNodes().size());
-    }
-    NodeInfo node = response.getNodes().get(0);
-    return node.getHttp().address().boundAddresses()[0];
-  }
-
-  /**
-   * Exposes an Elasticsearch
-   * <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).
-   *
-   * @return ES client API on a running instance
-   */
-  public Client client() {
-    Preconditions.checkState(isStarted, "node is not started");
-    return node.client();
-  }
-
-  @Override public void close() throws Exception {
-    node.close();
-    // cleanup data dirs
-    for (String name: Arrays.asList("path.data", "path.home")) {
-      if (node.settings().get(name) != null) {
-        File file = new File(node.settings().get(name));
-        if (file.exists()) {
-          file.delete();
-        }
-      }
-    }
-  }
-
-  /**
-   * 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']}.
-   *
-   * <p>This class is intended solely for tests
-   */
-  private static class LocalNode extends Node {
-
-    private LocalNode(Settings settings, Collection<Class<? extends Plugin>> classpathPlugins) {
-      super(InternalSettingsPreparer.prepareEnvironment(settings, null),
-              classpathPlugins);
-    }
-  }
-}
-
-// End EmbeddedElasticsearchNode.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchPolicy.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchPolicy.java b/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchPolicy.java
deleted file mode 100644
index 8cd2425..0000000
--- a/elasticsearch5/src/test/java/org/apache/calcite/adapter/elasticsearch5/EmbeddedElasticsearchPolicy.java
+++ /dev/null
@@ -1,102 +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.adapter.elasticsearch5;
-
-import org.elasticsearch.client.Client;
-import org.elasticsearch.common.transport.TransportAddress;
-import org.junit.rules.ExternalResource;
-
-import java.util.Objects;
-
-/**
- * Junit rule that is used to initialize a single Elasticsearch node for tests.
- *
- * <p>For performance reasons (node startup costs),
- * the same instance is usually shared across multiple tests.
- *
- * <p>This rule should be used as follows:
- * <pre>
- *
- *  public class MyTest {
- *    &#64;ClassRule
- *    public static final ElasticSearchRule RULE = ElasticSearchRule.create();
- *
- *    &#64;BeforeClass
- *    public static void setup() {
- *       // ... populate instance
- *    }
- *
- *    &#64;Test
- *    public void myTest() {
- *      TransportAddress address = RULE.httpAddress();
- *      // .... (connect to ES)
- *    }
- *  }
- * </pre>
- *
- * @see ExternalResource
- */
-class EmbeddedElasticsearchPolicy extends ExternalResource {
-
-  private final EmbeddedElasticsearchNode node;
-
-  private EmbeddedElasticsearchPolicy(EmbeddedElasticsearchNode resource) {
-    this.node = Objects.requireNonNull(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.
-   *
-   * @return new rule instance to be used in unit tests
-   */
-  public static EmbeddedElasticsearchPolicy create() {
-    return new EmbeddedElasticsearchPolicy(EmbeddedElasticsearchNode.create());
-  }
-
-  /**
-   * Exposes current ES transport client.
-   * @return running (and initialized) instance of ES node
-   */
-  Client client() {
-    return node.client();
-  }
-
-  /**
-   * HTTP address for rest clients (can be ES native or any other).
-   *
-   * @return {@code HTTP} connection parameters
-   */
-  TransportAddress httpAddress() {
-    return node.httpAddress();
-  }
-
-
-}
-
-// End EmbeddedElasticsearchPolicy.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/java/org/apache/calcite/test/Elasticsearch5AdapterIT.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/java/org/apache/calcite/test/Elasticsearch5AdapterIT.java b/elasticsearch5/src/test/java/org/apache/calcite/test/Elasticsearch5AdapterIT.java
deleted file mode 100644
index c727441..0000000
--- a/elasticsearch5/src/test/java/org/apache/calcite/test/Elasticsearch5AdapterIT.java
+++ /dev/null
@@ -1,265 +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.collect.ImmutableMap;
-
-import org.junit.Test;
-
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Tests for the {@code org.apache.calcite.adapter.elasticsearch} 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 Elasticsearch5AdapterIT {
-  /**
-   * 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",
-      Elasticsearch5AdapterIT.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 Consumer<List> elasticsearchChecker(final String... strings) {
-    return actual -> {
-      Object[] actualArray = actual == null || actual.isEmpty() ? null
-          : ((List) actual.get(0)).toArray();
-      CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
-          actualArray);
-    };
-  }
-
-  @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 Elasticsearch5AdapterIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java b/elasticsearch5/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
deleted file mode 100644
index be236ed..0000000
--- a/elasticsearch5/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
+++ /dev/null
@@ -1,45 +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 java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Utility methods for Elasticsearch tests.
- */
-public class ElasticsearchChecker {
-
-  private ElasticsearchChecker() {}
-
-  /** Returns a function that checks that a particular Elasticsearch pipeline is
-   * generated to implement a query.
-   *
-   * @param strings list of expected queries
-   * @return function to perform the check
-   */
-  public static Consumer<List> elasticsearchChecker(final String... strings) {
-    return actual -> {
-      Object[] actualArray = actual == null || actual.isEmpty() ? null
-          : ((List) actual.get(0)).toArray();
-      CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
-          actualArray);
-    };
-  }
-}
-
-// End ElasticsearchChecker.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/resources/log4j.properties b/elasticsearch5/src/test/resources/log4j.properties
deleted file mode 100644
index 834e2db..0000000
--- a/elasticsearch5/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,24 +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.
-
-# Root logger is configured at INFO and is sent to A1
-log4j.rootLogger=INFO, A1
-
-# A1 goes to the console
-log4j.appender.A1=org.apache.log4j.ConsoleAppender
-
-# Set the pattern for each log message
-log4j.appender.A1.layout=org.apache.log4j.PatternLayout
-log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p - %m%n

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/resources/log4j2.xml
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/resources/log4j2.xml b/elasticsearch5/src/test/resources/log4j2.xml
deleted file mode 100644
index 47b1613..0000000
--- a/elasticsearch5/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<Configuration status="WARN">
-
-    <Appenders>
-        <Console name="console" target="SYSTEM_OUT">
-            <PatternLayout
-                    pattern="[%-5level] %d{HH:mm:ss.SSS} %logger{36}.%M() - %msg%n" />
-        </Console>
-    </Appenders>
-
-    <Loggers>
-        <Root level="INFO" additivity="false">
-            <AppenderRef ref="console" />
-        </Root>
-    </Loggers>
-</Configuration>

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/test/resources/zips-mini.json
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/resources/zips-mini.json b/elasticsearch5/src/test/resources/zips-mini.json
deleted file mode 100644
index 858117a..0000000
--- a/elasticsearch5/src/test/resources/zips-mini.json
+++ /dev/null
@@ -1,149 +0,0 @@
-{ "_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" }

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index bea1327..60d4daa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,8 +67,7 @@ limitations under the License.
     <commons-lang3.version>3.2</commons-lang3.version>
     <commons-logging.version>1.1.3</commons-logging.version>
     <commons-pool2.version>2.5.0</commons-pool2.version>
-    <elasticsearch-java-driver.version>2.4.6</elasticsearch-java-driver.version>
-    <elasticsearch5-java-driver.version>5.6.9</elasticsearch5-java-driver.version>
+    <elasticsearch.version>6.2.4</elasticsearch.version>
     <esri-geometry-api.version>2.0.0</esri-geometry-api.version>
     <findbugs.version>3.0.1</findbugs.version>
     <fmpp-maven-plugin.version>1.0</fmpp-maven-plugin.version>
@@ -89,7 +88,6 @@ limitations under the License.
     <!-- Require Hadoop 2.7.4+ due to HADOOP-14586 -->
     <hadoop.version>2.7.5</hadoop.version>
     <hamcrest.version>1.3</hamcrest.version>
-    <hppc.version>0.7.1</hppc.version>
     <hsqldb.version>2.4.0</hsqldb.version>
     <httpclient.version>4.5.2</httpclient.version>
     <httpcore.version>4.4.4</httpcore.version>
@@ -145,7 +143,7 @@ limitations under the License.
       org.apache.calcite.sql.parser.impl,
       org.apache.calcite.sql.parser.parserextensiontesting,
       org.apache.calcite.piglet.parser,
-      org.openjdk.jmh,org.apache.calcite.adapter.elasticsearch2
+      org.openjdk.jmh
     </maven-javadoc-plugin.excludePackageNames>
     <maven-javadoc-plugin.link>https://docs.oracle.com/javase/9/docs/api/</maven-javadoc-plugin.link>
   </properties>
@@ -167,8 +165,7 @@ limitations under the License.
     <module>cassandra</module>
     <module>core</module>
     <module>druid</module>
-    <module>elasticsearch2</module>
-    <module>elasticsearch5</module>
+    <module>elasticsearch</module>
     <module>example</module>
     <module>file</module>
     <module>geode</module>
@@ -240,11 +237,6 @@ limitations under the License.
         <version>${commons-pool2.version}</version>
       </dependency>
       <dependency>
-        <groupId>com.carrotsearch</groupId>
-        <artifactId>hppc</artifactId>
-        <version>${hppc.version}</version>
-      </dependency>
-      <dependency>
         <groupId>com.datastax.cassandra</groupId>
         <artifactId>cassandra-driver-core</artifactId>
         <version>${cassandra-driver-core.version}</version>
@@ -474,12 +466,7 @@ limitations under the License.
       <dependency>
         <groupId>org.elasticsearch</groupId>
         <artifactId>elasticsearch</artifactId>
-        <version>${elasticsearch-java-driver.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.elasticsearch.module</groupId>
-        <artifactId>lang-groovy</artifactId>
-        <version>${elasticsearch-java-driver.version}</version>
+        <version>${elasticsearch.version}</version>
       </dependency>
       <dependency>
         <groupId>org.mongodb</groupId>


[2/5] calcite git commit: [CALCITE-2376] Unify ES2 and ES5 adapters. Migrate to low-level ES rest client as main transport. (Andrei Sereda)

Posted by mm...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 8624ac7..0000000
--- a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2AdapterTest.java
+++ /dev/null
@@ -1,398 +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.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.ElasticsearchChecker;
-
-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 the Elasticsearch 2 adapter.
- *
- * <p>Uses a real instance via {@link EmbeddedElasticsearchPolicy}.
- * The document source is a local {@code zips-mini.json} file
- * (located in the classpath).
- */
-public class Elasticsearch2AdapterTest {
-
-  @ClassRule // init once for all tests
-  public static final EmbeddedElasticsearchPolicy NODE =
-      EmbeddedElasticsearchPolicy.create();
-
-  private static final String ZIPS = "zips";
-
-  /**
-   * Used to create {@code zips} index and insert some data
-   *
-   * @throws Exception when couldn't create the instance
-   */
-  @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() {
-    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(
-            ElasticsearchChecker.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(
-            ElasticsearchChecker.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(
-            ElasticsearchChecker.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(
-            ElasticsearchChecker.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(
-            ElasticsearchChecker.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(ElasticsearchChecker.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(
-            ElasticsearchChecker.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/98838180/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchNode.java
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchNode.java b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchNode.java
deleted file mode 100644
index de150be..0000000
--- a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchNode.java
+++ /dev/null
@@ -1,155 +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.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;
-import java.util.Objects;
-
-/**
- * Represents a single Elasticsearch node that can run embedded in a java application.
- * Intended for unit and integration tests. Settings and plugins are crafted for Calcite.
- */
-class EmbeddedElasticsearchNode implements AutoCloseable {
-
-  private final LocalNode node;
-  private volatile boolean  isStarted;
-
-  private EmbeddedElasticsearchNode(LocalNode node) {
-    this.node = Objects.requireNonNull(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
-   *
-   * @param settings ES settings for the node
-   * @return un-started node; call {@link #start()} to start the instance
-   */
-  private static EmbeddedElasticsearchNode 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 EmbeddedElasticsearchNode(node);
-  }
-
-  /**
-   * Creates elastic node as single member of a cluster. Node will not be started
-   * unless {@link #start()} is explicitly called.
-   *
-   * @return node with default configuration
-   */
-  public static EmbeddedElasticsearchNode 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 the current address to connect to with HTTP client.
-   *
-   * @return {@code HTTP} address (hostname / port)
-   */
-  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 an Elasticsearch
-   * <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).
-   *
-   * @return client API to access ES functionality
-   */
-  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 EmbeddedElasticsearchNode.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchPolicy.java
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchPolicy.java b/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchPolicy.java
deleted file mode 100644
index 654d693..0000000
--- a/elasticsearch2/src/test/java/org/apache/calcite/adapter/elasticsearch2/EmbeddedElasticsearchPolicy.java
+++ /dev/null
@@ -1,102 +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.adapter.elasticsearch2;
-
-import org.elasticsearch.client.Client;
-import org.elasticsearch.common.transport.TransportAddress;
-import org.junit.rules.ExternalResource;
-
-import java.util.Objects;
-
-/**
- * Junit rule that is used to initialize a single Elasticsearch node for tests.
- *
- * <p>For performance reasons (node startup costs),
- * the same instance is usually shared across multiple tests.
- *
- * <p>This rule should be used as follows:
- * <pre>
- *
- *  public class MyTest {
- *    &#64;ClassRule
- *    public static final EmbeddedElasticsearchPolicy POLICY =
- *        EmbeddedElasticsearchPolicy.create();
- *
- *    &#64;BeforeClass
- *    public static void setup() {
- *       // ... populate instance
- *    }
- *
- *    &#64;Test
- *    public void myTest() {
- *      TransportAddress address = POLICY.httpAddress();
- *      // .... (connect)
- *    }
- * }
- * </pre>
- *
- * @see ExternalResource
- */
-class EmbeddedElasticsearchPolicy extends ExternalResource {
-
-  private final EmbeddedElasticsearchNode node;
-
-  private EmbeddedElasticsearchPolicy(EmbeddedElasticsearchNode resource) {
-    this.node = Objects.requireNonNull(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.
-   *
-   * @return new rule instance to be used in unit tests
-   */
-  public static EmbeddedElasticsearchPolicy create() {
-    return new EmbeddedElasticsearchPolicy(EmbeddedElasticsearchNode.create());
-  }
-
-  /**
-   * Exposes current ES transport client.
-   *
-   * @return initialized instance of ES
-   */
-  Client client() {
-    return node.client();
-  }
-
-  /**
-   * HTTP address for rest clients (can be ES native or any other).
-   *
-   * @return HTTP hostname/port to connect to this ES instance
-   */
-  TransportAddress httpAddress() {
-    return node.httpAddress();
-  }
-}
-
-// End EmbeddedElasticsearchPolicy.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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 f385d1d..0000000
--- a/elasticsearch2/src/test/java/org/apache/calcite/test/Elasticsearch2AdapterIT.java
+++ /dev/null
@@ -1,265 +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.collect.ImmutableMap;
-
-import org.junit.Test;
-
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * 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 Consumer<List> elasticsearchChecker(final String... strings) {
-    return actual -> {
-      Object[] actualArray = actual == null || actual.isEmpty() ? null
-          : ((List) actual.get(0)).toArray();
-      CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
-          actualArray);
-    };
-  }
-
-  @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/98838180/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java b/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
deleted file mode 100644
index be236ed..0000000
--- a/elasticsearch2/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
+++ /dev/null
@@ -1,45 +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 java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Utility methods for Elasticsearch tests.
- */
-public class ElasticsearchChecker {
-
-  private ElasticsearchChecker() {}
-
-  /** Returns a function that checks that a particular Elasticsearch pipeline is
-   * generated to implement a query.
-   *
-   * @param strings list of expected queries
-   * @return function to perform the check
-   */
-  public static Consumer<List> elasticsearchChecker(final String... strings) {
-    return actual -> {
-      Object[] actualArray = actual == null || actual.isEmpty() ? null
-          : ((List) actual.get(0)).toArray();
-      CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
-          actualArray);
-    };
-  }
-}
-
-// End ElasticsearchChecker.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch2/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/test/resources/log4j.properties b/elasticsearch2/src/test/resources/log4j.properties
deleted file mode 100644
index 834e2db..0000000
--- a/elasticsearch2/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,24 +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.
-
-# Root logger is configured at INFO and is sent to A1
-log4j.rootLogger=INFO, A1
-
-# A1 goes to the console
-log4j.appender.A1=org.apache.log4j.ConsoleAppender
-
-# Set the pattern for each log message
-log4j.appender.A1.layout=org.apache.log4j.PatternLayout
-log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p - %m%n

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 858117a..0000000
--- a/elasticsearch2/src/test/resources/zips-mini.json
+++ /dev/null
@@ -1,149 +0,0 @@
-{ "_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" }

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/pom.xml
----------------------------------------------------------------------
diff --git a/elasticsearch5/pom.xml b/elasticsearch5/pom.xml
deleted file mode 100644
index 9525cd5..0000000
--- a/elasticsearch5/pom.xml
+++ /dev/null
@@ -1,180 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.apache.calcite</groupId>
-    <artifactId>calcite</artifactId>
-    <version>1.17.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>calcite-elasticsearch5</artifactId>
-  <packaging>jar</packaging>
-  <version>1.17.0-SNAPSHOT</version>
-  <name>Calcite Elasticsearch5</name>
-  <description>Elasticsearch5 adapter for Calcite</description>
-
-  <properties>
-    <top.dir>${project.basedir}/..</top.dir>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.calcite</groupId>
-      <artifactId>calcite-core</artifactId>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.calcite</groupId>
-      <artifactId>calcite-core</artifactId>
-      <type>test-jar</type>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.calcite</groupId>
-      <artifactId>calcite-linq4j</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.elasticsearch.client</groupId>
-      <artifactId>transport</artifactId>
-      <version>${elasticsearch5-java-driver.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.elasticsearch</groupId>
-      <artifactId>elasticsearch</artifactId>
-      <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>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-dependency-plugin</artifactId>
-        <version>${maven-dependency-plugin.version}</version>
-        <executions>
-          <execution>
-            <id>analyze</id>
-            <goals>
-              <goal>analyze-only</goal>
-            </goals>
-            <configuration>
-              <failOnWarning>true</failOnWarning>
-              <!-- ignore "unused but declared" warnings -->
-              <ignoredUnusedDeclaredDependencies>
-                <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>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <goals>
-              <goal>test-jar</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-release-plugin</artifactId>
-      </plugin>
-      <!-- Parent module has the same plugin and does the work of
-          generating -sources.jar for each project. But without the
-          plugin declared here, IDEs don't know the sources are
-          available. -->
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-source-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>attach-sources</id>
-            <phase>verify</phase>
-            <goals>
-              <goal>jar-no-fork</goal>
-              <goal>test-jar-no-fork</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Enumerator.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Enumerator.java b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Enumerator.java
deleted file mode 100644
index 104840a..0000000
--- a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Enumerator.java
+++ /dev/null
@@ -1,147 +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.adapter.elasticsearch5;
-
-import org.apache.calcite.avatica.util.DateTimeUtils;
-import org.apache.calcite.linq4j.Enumerator;
-import org.apache.calcite.linq4j.function.Function1;
-import org.apache.calcite.linq4j.tree.Primitive;
-
-import org.elasticsearch.search.SearchHit;
-
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Enumerator that reads from an Elasticsearch type.
- */
-public class Elasticsearch5Enumerator implements Enumerator<Object> {
-  private final Iterator<SearchHit> cursor;
-  private final Function1<SearchHit, Object> getter;
-  private Object current;
-
-  /**
-   * Creates an Elasticsearch5Enumerator.
-   *
-   * @param cursor Iterator over Elasticsearch {@link SearchHit} objects
-   * @param getter Converts an object into a list of fields
-   */
-  public Elasticsearch5Enumerator(Iterator<SearchHit> cursor,
-      Function1<SearchHit, Object> getter) {
-    this.cursor = cursor;
-    this.getter = getter;
-  }
-
-  public Object current() {
-    return current;
-  }
-
-  public boolean moveNext() {
-    if (cursor.hasNext()) {
-      SearchHit map = cursor.next();
-      current = getter.apply(map);
-      return true;
-    } else {
-      current = null;
-      return false;
-    }
-  }
-
-  public void reset() {
-    throw new UnsupportedOperationException();
-  }
-
-  public void close() {
-    // nothing to do
-  }
-
-  private static Function1<SearchHit, Map> mapGetter() {
-    return searchHitFields -> (Map) searchHitFields.getFields();
-  }
-
-  private static Function1<SearchHit, Object> singletonGetter(final String fieldName,
-      final Class fieldClass) {
-    return searchHitFields -> {
-      if (searchHitFields.getFields().isEmpty()) {
-        return convert(searchHitFields.getSource(), fieldClass);
-      } else {
-        return convert(searchHitFields.getFields(), fieldClass);
-      }
-    };
-  }
-
-  /**
-   * Function that extracts a given set of fields from {@link SearchHit}
-   * objects.
-   *
-   * @param fields List of fields to project
-   * @return conversion function to transform ES search result into a generic array
-   */
-  private static Function1<SearchHit, Object[]> listGetter(
-      final List<Map.Entry<String, Class>> fields) {
-    return searchHitFields -> {
-      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.getFields().isEmpty()) {
-          objects[i] = convert(searchHitFields.getSource().get(name),
-              field.getValue());
-        } else {
-          objects[i] = convert(searchHitFields.getField(name).getValue(),
-              field.getValue());
-        }
-      }
-      return objects;
-    };
-  }
-
-  static Function1<SearchHit, Object> getter(List<Map.Entry<String, Class>> fields) {
-    //noinspection unchecked
-    return fields == null
-      ? (Function1) mapGetter()
-      : fields.size() == 1
-      ? singletonGetter(fields.get(0).getKey(), fields.get(0).getValue())
-      : (Function1) listGetter(fields);
-  }
-
-  private static Object convert(Object o, Class clazz) {
-    if (o == null) {
-      return null;
-    }
-    Primitive primitive = Primitive.of(clazz);
-    if (primitive != null) {
-      clazz = primitive.boxClass;
-    } else {
-      primitive = Primitive.ofBox(clazz);
-    }
-    if (clazz.isInstance(o)) {
-      return o;
-    }
-    if (o instanceof Date && primitive != null) {
-      o = ((Date) o).getTime() / DateTimeUtils.MILLIS_PER_DAY;
-    }
-    if (o instanceof Number && primitive != null) {
-      return primitive.number((Number) o);
-    }
-    return o;
-  }
-}
-
-// End Elasticsearch5Enumerator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index adf0fa2..0000000
--- a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Schema.java
+++ /dev/null
@@ -1,154 +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.adapter.elasticsearch5;
-
-import org.apache.calcite.adapter.elasticsearch.ElasticsearchSchema;
-import org.apache.calcite.schema.Table;
-import org.apache.calcite.schema.impl.AbstractSchema;
-
-import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
-import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
-import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
-import org.elasticsearch.client.Client;
-import org.elasticsearch.client.transport.TransportClient;
-import org.elasticsearch.cluster.metadata.MappingMetaData;
-import org.elasticsearch.cluster.node.DiscoveryNode;
-import org.elasticsearch.common.collect.ImmutableOpenMap;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.transport.InetSocketTransportAddress;
-import org.elasticsearch.common.transport.TransportAddress;
-import org.elasticsearch.transport.client.PreBuiltTransportClient;
-
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Schema mapped onto an index of ELASTICSEARCH types.
- *
- * <p>Each table in the schema is an ELASTICSEARCH type in that index.
- */
-public class Elasticsearch5Schema extends AbstractSchema
-    implements ElasticsearchSchema {
-  final String index;
-
-  private transient Client client;
-
-  /**
-   * Creates an Elasticsearch5 schema.
-   *
-   * @param coordinates Map of Elasticsearch node locations (host, port)
-   * @param userConfig Map of user-specified configurations
-   * @param indexName Elasticsearch database name, e.g. "usa".
-   */
-  Elasticsearch5Schema(Map<String, Integer> coordinates,
-      Map<String, String> userConfig, String indexName) {
-    super();
-
-    final List<InetSocketAddress> transportAddresses = new ArrayList<>();
-    for (Map.Entry<String, Integer> coordinate: coordinates.entrySet()) {
-      transportAddresses.add(
-          new InetSocketAddress(coordinate.getKey(), coordinate.getValue()));
-    }
-
-    open(transportAddresses, userConfig);
-
-    if (client != null) {
-      final String[] indices = client.admin().indices()
-          .getIndex(new GetIndexRequest().indices(indexName))
-          .actionGet().getIndices();
-      if (indices.length == 1) {
-        index = indices[0];
-      } else {
-        index = null;
-      }
-    } else {
-      index = null;
-    }
-  }
-
-  /**
-   * Allows schema to be instantiated from existing elastic search client.
-   * This constructor is used in tests.
-   * @param client existing client instance
-   * @param index name of ES index
-   */
-  @VisibleForTesting
-  Elasticsearch5Schema(Client client, String index) {
-    this.client = Objects.requireNonNull(client, "client");
-    this.index = Objects.requireNonNull(index, "index");
-  }
-
-
-  @Override protected Map<String, Table> getTableMap() {
-    final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
-
-    try {
-      final GetMappingsResponse response = client.admin().indices()
-          .getMappings(new GetMappingsRequest().indices(index))
-          .get();
-      ImmutableOpenMap<String, MappingMetaData> mapping =
-          response.getMappings().get(index);
-      for (ObjectObjectCursor<String, MappingMetaData> c : mapping) {
-        builder.put(c.key, new Elasticsearch5Table(client, index, c.key));
-      }
-    } catch (RuntimeException e) {
-      throw e;
-    } catch (Exception e) {
-      throw new RuntimeException(e);
-    }
-    return builder.build();
-  }
-
-  private void open(List<InetSocketAddress> transportAddresses,
-      Map<String, String> userConfig) {
-    final List<TransportAddress> transportNodes = new ArrayList<>(transportAddresses.size());
-    for (InetSocketAddress address : transportAddresses) {
-      transportNodes.add(new InetSocketTransportAddress(address));
-    }
-
-    Settings settings = Settings.builder().put(userConfig).build();
-
-    final TransportClient transportClient = new PreBuiltTransportClient(settings);
-    for (TransportAddress transport : transportNodes) {
-      transportClient.addTransportAddress(transport);
-    }
-
-    final List<DiscoveryNode> nodes =
-        ImmutableList.copyOf(transportClient.connectedNodes());
-
-    if (nodes.isEmpty()) {
-      throw new IllegalStateException("Cannot connect to any elasticsearch node from "
-              + transportNodes);
-    }
-
-    client = transportClient;
-  }
-
-  @Override public String getIndex() {
-    return index;
-  }
-}
-
-// End Elasticsearch5Schema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5SchemaFactory.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5SchemaFactory.java b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5SchemaFactory.java
deleted file mode 100644
index b573d37..0000000
--- a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5SchemaFactory.java
+++ /dev/null
@@ -1,63 +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.adapter.elasticsearch5;
-
-import org.apache.calcite.schema.Schema;
-import org.apache.calcite.schema.SchemaFactory;
-import org.apache.calcite.schema.SchemaPlus;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * Factory that creates an {@link Elasticsearch5Schema}.
- *
- * <p>Allows a custom schema to be included in a model.json file.
- */
-@SuppressWarnings("UnusedDeclaration")
-public class Elasticsearch5SchemaFactory implements SchemaFactory {
-
-  public Elasticsearch5SchemaFactory() {
-  }
-
-  @Override public Schema create(SchemaPlus parentSchema, String name,
-      Map<String, Object> operand) {
-    final Map map = (Map) operand;
-
-    final ObjectMapper mapper = new ObjectMapper();
-    mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
-
-    try {
-      final Map<String, Integer> coordinates =
-          mapper.readValue((String) map.get("coordinates"),
-              new TypeReference<Map<String, Integer>>() { });
-      final Map<String, String> userConfig =
-          mapper.readValue((String) map.get("userConfig"),
-              new TypeReference<Map<String, String>>() { });
-      final String index = (String) map.get("index");
-      return new Elasticsearch5Schema(coordinates, userConfig, index);
-    } catch (IOException e) {
-      throw new RuntimeException("Cannot parse values from json", e);
-    }
-  }
-}
-
-// End Elasticsearch5SchemaFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 96bef62..0000000
--- a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Table.java
+++ /dev/null
@@ -1,96 +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.adapter.elasticsearch5;
-
-import org.apache.calcite.adapter.elasticsearch.AbstractElasticsearchTable;
-import org.apache.calcite.linq4j.AbstractEnumerable;
-import org.apache.calcite.linq4j.Enumerable;
-import org.apache.calcite.linq4j.Enumerator;
-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;
-
-/**
- * Table based on an Elasticsearch5 type.
- */
-public class Elasticsearch5Table extends AbstractElasticsearchTable {
-  private final Client client;
-
-  /**
-   * Creates an Elasticsearch5Table.
-   * @param client existing ES instance
-   * @param indexName ES index name
-   * @param typeName ES type
-   */
-  Elasticsearch5Table(Client client, String indexName, String typeName) {
-    super(indexName, typeName);
-    this.client = client;
-  }
-
-  @Override protected Enumerable<Object> find(String index, List<String> ops,
-      List<Map.Entry<String, Class>> fields) {
-    final String dbName = index;
-
-    final SearchSourceBuilder searchSourceBuilder;
-    if (ops.isEmpty()) {
-      searchSourceBuilder = new SearchSourceBuilder();
-    } else {
-      String queryString = "{" + Util.toString(ops, "", ", ", "") + "}";
-      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 UncheckedIOException(ex);
-      }
-    }
-    final Function1<SearchHit, Object> getter = Elasticsearch5Enumerator.getter(fields);
-
-    return new AbstractEnumerable<Object>() {
-      public Enumerator<Object> enumerator() {
-        final Iterator<SearchHit> cursor = client.prepareSearch(dbName).setTypes(typeName)
-            .setSource(searchSourceBuilder)
-            .execute().actionGet().getHits().iterator();
-        return new Elasticsearch5Enumerator(cursor, getter);
-      }
-    };
-  }
-}
-
-// End Elasticsearch5Table.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/package-info.java
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/package-info.java b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/package-info.java
deleted file mode 100644
index 70c4695..0000000
--- a/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/package-info.java
+++ /dev/null
@@ -1,26 +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.
- */
-
-/**
- * Query provider based on an Elasticsearch5 DB.
- */
-@PackageMarker
-package org.apache.calcite.adapter.elasticsearch5;
-
-import org.apache.calcite.avatica.util.PackageMarker;
-
-// End package-info.java


[3/5] calcite git commit: [CALCITE-2376] Unify ES2 and ES5 adapters. Migrate to low-level ES rest client as main transport. (Andrei Sereda)

Posted by mm...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticSearchAdapterTest.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticSearchAdapterTest.java b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticSearchAdapterTest.java
new file mode 100644
index 0000000..456fbac
--- /dev/null
+++ b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticSearchAdapterTest.java
@@ -0,0 +1,400 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import 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.ElasticsearchChecker;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.util.EntityUtils;
+
+import com.google.common.io.LineProcessor;
+import com.google.common.io.Resources;
+
+import org.elasticsearch.client.Response;
+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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Set of tests for ES adapter. Uses real instance via {@link EmbeddedElasticsearchPolicy}. Document
+ * source is local {@code zips-mini.json} file (located in test classpath).
+ */
+public class ElasticSearchAdapterTest {
+
+  @ClassRule //init once for all tests
+  public static final EmbeddedElasticsearchPolicy NODE = EmbeddedElasticsearchPolicy.create();
+
+  /** Default index/type name */
+  private static final String ZIPS = "zips";
+
+  /**
+   * Used to create {@code zips} index and insert zip data in bulk.
+   * @throws Exception when instance setup failed
+   */
+  @BeforeClass
+  public static void setupInstance() throws Exception {
+    // hardcoded mapping definition
+    final String mapping = String.format(Locale.ROOT,
+        "{'mappings':{'%s':{'properties':"
+            + "{'city':{'type':'keyword'},'state':{'type':'keyword'},'pop':{'type':'long'}}"
+            + "}}}", ZIPS).replace('\'', '"');
+
+    // create index and mapping
+    final HttpEntity entity = new StringEntity(mapping, ContentType.APPLICATION_JSON);
+    NODE.restClient().performRequest("PUT", "/" + ZIPS, Collections.emptyMap(), entity);
+
+    // load records from file
+    final List<String> bulk = new ArrayList<>();
+    Resources.readLines(ElasticSearchAdapterTest.class.getResource("/zips-mini.json"),
+        StandardCharsets.UTF_8, new LineProcessor<Void>() {
+          @Override public boolean processLine(String line) throws IOException {
+            bulk.add("{\"index\": {} }"); // index/type will be derived from _bulk URI
+            line = line.replaceAll("_id", "id"); // _id is a reserved attribute in ES
+            bulk.add(line);
+            return true;
+          }
+
+          @Override public Void getResult() {
+            return null;
+          }
+        });
+
+    if (bulk.isEmpty()) {
+      throw new IllegalStateException("No records to index. Empty file ?");
+    }
+
+    final String uri = String.format(Locale.ROOT, "/%s/%s/_bulk?refresh", ZIPS, ZIPS);
+    Response response = NODE.restClient().performRequest("POST", uri,
+        Collections.emptyMap(),
+        new StringEntity(String.join("\n", bulk) + "\n", ContentType.APPLICATION_JSON));
+
+    if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+      final String error = EntityUtils.toString(response.getEntity());
+      final String message = String.format(Locale.ROOT,
+          "Couldn't bulk insert %d elements into %s (%s/%s). Error was %s\n%s\n",
+          bulk.size(), ZIPS, response.getHost(),
+          response.getRequestLine(),
+          response.getStatusLine(), error);
+
+      throw new IllegalStateException(message);
+    }
+
+  }
+
+  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 ElasticsearchSchema(NODE.restClient(), NODE.mapper(), 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() {
+    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\", \"pop\" from zips\n"
+        + "order by \"state\", \"pop\" offset 2 rows fetch next 3 rows only";
+    calciteAssert()
+        .query(sql)
+        .returnsUnordered("state=AK; pop=32383",
+            "state=AL; pop=42124",
+            "state=AL; pop=43862")
+        .queryContains(
+            ElasticsearchChecker.elasticsearchChecker(
+                "\"_source\" : [\"state\", \"pop\"]",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"pop\": \"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(
+            ElasticsearchChecker.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(
+            ElasticsearchChecker.elasticsearchChecker(
+                "\"size\": 3",
+                "\"_source\" : [\"state\", \"id\"]"));
+  }
+
+  @Test public void testFilterSort() {
+    final String sql = "select * from zips\n"
+        + "where \"state\" = 'CA' and \"pop\" >= 94000\n"
+        + "order by \"state\", \"pop\"";
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchSort(sort0=[$4], sort1=[$3], 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, 'pop')):INTEGER, 94000))])\n"
+        + "        ElasticsearchTableScan(table=[[elastic, zips]])\n\n";
+    calciteAssert()
+        .query(sql)
+        .returnsOrdered("city=NORWALK; longitude=-118.081767; latitude=33.90564;"
+                + " pop=94188; state=CA; id=90650",
+            "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")
+        .queryContains(
+            ElasticsearchChecker.elasticsearchChecker("\"query\" : "
+                    + "{\"constant_score\":{\"filter\":{\"bool\":"
+                    + "{\"must\":[{\"term\":{\"state\":\"CA\"}},"
+                    + "{\"range\":{\"pop\":{\"gte\":94000}}}]}}}}",
+                "\"script_fields\": {\"longitude\":{\"script\":\"params._source.loc[0]\"}, "
+                    + "\"latitude\":{\"script\":\"params._source.loc[1]\"}, "
+                    + "\"city\":{\"script\": \"params._source.city\"}, "
+                    + "\"pop\":{\"script\": \"params._source.pop\"}, "
+                    + "\"state\":{\"script\": \"params._source.state\"}, "
+                    + "\"id\":{\"script\": \"params._source.id\"}}",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"pop\": \"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(
+            ElasticsearchChecker.elasticsearchChecker(""
+                    + "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
+                    + "{\"must\":[{\"term\":{\"state\":\"OK\"}}]}}}}",
+                "\"script_fields\": {\"longitude\":{\"script\":\"params._source.loc[0]\"}, "
+                    +  "\"latitude\":{\"script\":\"params._source.loc[1]\"}, "
+                    +   "\"city\":{\"script\": \"params._source.city\"}, "
+                    +   "\"pop\":{\"script\": \"params._source.pop\"}, \"state\":{\"script\": \"params._source.state\"}, "
+                    +            "\"id\":{\"script\": \"params._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\":\"params._source.loc[0]\"}, "
+            +  "\"latitude\":{\"script\":\"params._source.loc[1]\"}, "
+            +  "\"city\":{\"script\": \"params._source.city\"}, "
+            +  "\"pop\":{\"script\": \"params._source.pop\"}, "
+            +  "\"state\":{\"script\": \"params._source.state\"}, "
+            +  "\"id\":{\"script\": \"params._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(ElasticsearchChecker.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(
+            ElasticsearchChecker.elasticsearchChecker("\"script_fields\": "
+                    + "{\"zero\":{\"script\": \"0\"}, "
+                    + "\"state\":{\"script\": \"params._source.state\"}, "
+                    + "\"city\":{\"script\": \"params._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 ElasticSearchAdapterTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersionTest.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersionTest.java b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersionTest.java
new file mode 100644
index 0000000..b25d76b
--- /dev/null
+++ b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersionTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.junit.Test;
+
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Basic tests for parsing ES version in different formats
+ */
+public class ElasticsearchVersionTest {
+
+  @Test
+  public void versions() {
+    assertEquals(ElasticsearchVersion.fromString("2.3.4"), ElasticsearchVersion.ES2);
+    assertEquals(ElasticsearchVersion.fromString("2.0.0"), ElasticsearchVersion.ES2);
+    assertEquals(ElasticsearchVersion.fromString("5.6.1"), ElasticsearchVersion.ES5);
+    assertEquals(ElasticsearchVersion.fromString("6.0.1"), ElasticsearchVersion.ES6);
+    assertEquals(ElasticsearchVersion.fromString("7.0.1"), ElasticsearchVersion.ES7);
+    assertEquals(ElasticsearchVersion.fromString("111.0.1"), ElasticsearchVersion.UNKNOWN);
+
+    assertFails("");
+    assertFails(".");
+    assertFails(".1.2");
+    assertFails("1.2");
+    assertFails("0");
+    assertFails("b");
+    assertFails("a.b");
+    assertFails("aa");
+    assertFails("a.b.c");
+    assertFails("2.2");
+    assertFails("a.2");
+    assertFails("2.2.0a");
+    assertFails("2a.2.0");
+  }
+
+  private static void assertFails(String version) {
+    try {
+      ElasticsearchVersion.fromString(version);
+      fail(String.format(Locale.ROOT, "Should fail for version %s", version));
+    } catch (IllegalArgumentException ignore) {
+      // expected
+    }
+  }
+}
+
+// End ElasticsearchVersionTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchNode.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchNode.java b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchNode.java
new file mode 100644
index 0000000..5dfb1f6
--- /dev/null
+++ b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchNode.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import com.google.common.base.Preconditions;
+import com.google.common.io.Files;
+
+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.InternalSettingsPreparer;
+import org.elasticsearch.node.Node;
+import org.elasticsearch.node.NodeValidationException;
+import org.elasticsearch.painless.PainlessPlugin;
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.transport.Netty4Plugin;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * 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 EmbeddedElasticsearchNode implements AutoCloseable {
+
+  private final Node node;
+  private volatile boolean  isStarted;
+
+  private EmbeddedElasticsearchNode(Node node) {
+    this.node = Objects.requireNonNull(node, "node");
+  }
+
+
+  /**
+   * Creates an instance with existing settings
+   * @param settings configuration parameters of ES instance
+   * @return instance which needs to be explicitly started (using {@link #start()})
+   */
+  private static EmbeddedElasticsearchNode create(Settings settings) {
+    // ensure GroovyPlugin is installed or otherwise scripted fields would not work
+    Node node = new LocalNode(settings, Arrays.asList(Netty4Plugin.class, PainlessPlugin.class));
+    return new EmbeddedElasticsearchNode(node);
+  }
+
+  /**
+   * Creates elastic node as single member of a cluster. Node will not be started
+   * unless {@link #start()} is explicitly called.
+   * @return instance which needs to be explicitly started (using {@link #start()})
+   */
+  public static EmbeddedElasticsearchNode 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("http.type", "netty4")
+        .put("network.host", "localhost")
+        .build();
+
+    return create(settings);
+  }
+
+  /**
+   * Starts current node
+   */
+  public void start() {
+    Preconditions.checkState(!isStarted, "already started");
+    try {
+      node.start();
+      this.isStarted = true;
+    } catch (NodeValidationException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  /**
+   * Returns current address to connect to with HTTP client.
+   * @return hostname/port for HTTP connection
+   */
+  public TransportAddress httpAddress() {
+    Preconditions.checkState(isStarted, "node is not started");
+
+    NodesInfoResponse response =  client().admin().cluster().prepareNodesInfo()
+        .execute().actionGet();
+    if (response.getNodes().size() != 1) {
+      throw new IllegalStateException("Expected single node but got "
+          + response.getNodes().size());
+    }
+    NodeInfo node = response.getNodes().get(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).
+   * @return current elastic search client
+   */
+  public Client client() {
+    Preconditions.checkState(isStarted, "node is not started");
+    return node.client();
+  }
+
+  @Override public void close() throws Exception {
+    node.close();
+    // cleanup data dirs
+    for (String name: Arrays.asList("path.data", "path.home")) {
+      if (node.settings().get(name) != null) {
+        File file = new File(node.settings().get(name));
+        if (file.exists()) {
+          file.delete();
+        }
+      }
+    }
+  }
+
+  /**
+   * 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']}.
+   *
+   * <p>This class is intended solely for tests
+   */
+  private static class LocalNode extends Node {
+
+    private LocalNode(Settings settings, Collection<Class<? extends Plugin>> classpathPlugins) {
+      super(InternalSettingsPreparer.prepareEnvironment(settings, null),
+          classpathPlugins);
+    }
+  }
+}
+
+// End EmbeddedElasticsearchNode.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchPolicy.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchPolicy.java b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchPolicy.java
new file mode 100644
index 0000000..1ef9175
--- /dev/null
+++ b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/EmbeddedElasticsearchPolicy.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.util.Closer;
+
+import org.apache.http.HttpHost;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.common.transport.TransportAddress;
+import org.junit.rules.ExternalResource;
+
+import java.util.Objects;
+
+/**
+ * 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>
+ *  public class MyTest {
+ *    &#64;ClassRule
+ *    public static final EmbeddedElasticsearchPolicy RULE = EmbeddedElasticsearchPolicy.create();
+ *
+ *    &#64;BeforeClass
+ *    public static void setup() {
+ *       // ... populate instance
+ *    }
+ *
+ *    &#64;Test
+ *    public void myTest() {
+ *      TransportAddress address = RULE.httpAddress();
+ *      // ....
+ *    }
+ *  }
+ *  </pre>
+ * @see ExternalResource
+ */
+class EmbeddedElasticsearchPolicy extends ExternalResource {
+
+  private final EmbeddedElasticsearchNode node;
+  private final ObjectMapper mapper;
+  private final Closer closer;
+  private RestClient client;
+
+  private EmbeddedElasticsearchPolicy(EmbeddedElasticsearchNode resource) {
+    this.node = Objects.requireNonNull(resource, "resource");
+    this.mapper = new ObjectMapper();
+    this.closer = new Closer();
+    closer.add(node);
+  }
+
+  @Override protected void before() throws Throwable {
+    node.start();
+  }
+
+  @Override protected void after() {
+    closer.close();
+  }
+
+  /**
+   * Factory method to create this rule.
+   * @return managed resource to be used in unit tests
+   */
+  public static EmbeddedElasticsearchPolicy create() {
+    return new EmbeddedElasticsearchPolicy(EmbeddedElasticsearchNode.create());
+  }
+
+  /**
+   * Exposes Jackson API to be used for low-level ES.
+   * @return existing instance of ObjectMapper
+   */
+  ObjectMapper mapper() {
+    return mapper;
+  }
+
+  /**
+   * Low-level http rest client to elastic search
+   * @return current ES rest client
+   */
+  RestClient restClient() {
+    if (client != null) {
+      return client;
+    }
+    TransportAddress address = httpAddress();
+    RestClient client = RestClient.builder(new HttpHost(address.getAddress(), address.getPort()))
+            .build();
+    closer.add(client);
+    this.client = client;
+    return client;
+  }
+
+  /**
+   * HTTP address for rest clients (can be ES native or any other).
+   * @return http address to connect to
+   */
+  TransportAddress httpAddress() {
+    return node.httpAddress();
+  }
+
+}
+
+// End EmbeddedElasticsearchPolicy.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java b/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
new file mode 100644
index 0000000..68fc073
--- /dev/null
+++ b/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchChecker.java
@@ -0,0 +1,44 @@
+/*
+ * 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 java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Internal util methods for ElasticSearch tests
+ */
+public class ElasticsearchChecker {
+
+  private ElasticsearchChecker() {}
+
+  /** Returns a function that checks that a particular Elasticsearch pipeline is
+   * generated to implement a query.
+   * @param strings expected expressions
+   * @return validation function
+   */
+  public static Consumer<List> elasticsearchChecker(final String... strings) {
+    return actual -> {
+      Object[] actualArray = actual == null || actual.isEmpty() ? null
+          : ((List) actual.get(0)).toArray();
+      CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
+          actualArray);
+    };
+  }
+}
+
+// End ElasticsearchChecker.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/test/resources/log4j2.xml
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/resources/log4j2.xml b/elasticsearch/src/test/resources/log4j2.xml
new file mode 100644
index 0000000..c8d70aa
--- /dev/null
+++ b/elasticsearch/src/test/resources/log4j2.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ /*
+  ~  * 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.
+  ~  */
+  ~
+  -->
+
+<Configuration status="WARN">
+
+  <Appenders>
+    <Console name="console" target="SYSTEM_OUT">
+      <PatternLayout
+          pattern="[%-5level] %d{HH:mm:ss.SSS} %logger{36}.%M() - %msg%n" />
+    </Console>
+  </Appenders>
+
+  <Loggers>
+    <Root level="INFO" additivity="false">
+      <AppenderRef ref="console" />
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/test/resources/zips-mini.json
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/resources/zips-mini.json b/elasticsearch/src/test/resources/zips-mini.json
new file mode 100644
index 0000000..858117a
--- /dev/null
+++ b/elasticsearch/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" }

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch2/pom.xml
----------------------------------------------------------------------
diff --git a/elasticsearch2/pom.xml b/elasticsearch2/pom.xml
deleted file mode 100644
index a017f7a..0000000
--- a/elasticsearch2/pom.xml
+++ /dev/null
@@ -1,150 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.apache.calcite</groupId>
-    <artifactId>calcite</artifactId>
-    <version>1.17.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>calcite-elasticsearch2</artifactId>
-  <packaging>jar</packaging>
-  <version>1.17.0-SNAPSHOT</version>
-  <name>Calcite Elasticsearch</name>
-  <description>Elasticsearch adapter for Calcite</description>
-
-  <properties>
-    <top.dir>${project.basedir}/..</top.dir>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.calcite</groupId>
-      <artifactId>calcite-core</artifactId>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.calcite</groupId>
-      <artifactId>calcite-core</artifactId>
-      <type>test-jar</type>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.calcite</groupId>
-      <artifactId>calcite-linq4j</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.elasticsearch</groupId>
-      <artifactId>elasticsearch</artifactId>
-      <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>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-dependency-plugin</artifactId>
-        <version>${maven-dependency-plugin.version}</version>
-        <executions>
-          <execution>
-            <id>analyze</id>
-            <goals>
-              <goal>analyze-only</goal>
-            </goals>
-            <configuration>
-              <failOnWarning>true</failOnWarning>
-              <!-- ignore "unused but declared" warnings -->
-              <ignoredUnusedDeclaredDependencies>
-                <ignoredUnusedDeclaredDependency>org.apache.calcite.avatica:avatica</ignoredUnusedDeclaredDependency>
-                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-api</ignoredUnusedDeclaredDependency>
-                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
-              </ignoredUnusedDeclaredDependencies>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <executions>
-          <execution>
-            <goals>
-              <goal>test-jar</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-release-plugin</artifactId>
-      </plugin>
-      <!-- Parent module has the same plugin and does the work of
-          generating -sources.jar for each project. But without the
-          plugin declared here, IDEs don't know the sources are
-          available. -->
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-source-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>attach-sources</id>
-            <phase>verify</phase>
-            <goals>
-              <goal>jar-no-fork</goal>
-              <goal>test-jar-no-fork</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 54e4357..0000000
--- a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Enumerator.java
+++ /dev/null
@@ -1,150 +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.adapter.elasticsearch2;
-
-import org.apache.calcite.avatica.util.DateTimeUtils;
-import org.apache.calcite.linq4j.Enumerator;
-import org.apache.calcite.linq4j.function.Function1;
-import org.apache.calcite.linq4j.tree.Primitive;
-
-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;
-
-/**
- * Enumerator that reads from an Elasticsearch type.
- */
-public class Elasticsearch2Enumerator implements Enumerator<Object> {
-  private final Iterator<SearchHit> cursor;
-  private final Function1<SearchHit, Object> getter;
-  private Object current;
-
-  /**
-   * Creates an ElasticsearchEnumerator.
-   *
-   * @param cursor Iterator over Elasticsearch {@link SearchHit} objects
-   * @param getter Converts an object into a list of fields
-   */
-  public Elasticsearch2Enumerator(Iterator<SearchHit> cursor,
-      Function1<SearchHit, Object> getter) {
-    this.cursor = cursor;
-    this.getter = getter;
-  }
-
-  public Object current() {
-    return current;
-  }
-
-  public boolean moveNext() {
-    if (cursor.hasNext()) {
-      SearchHit map = cursor.next();
-      current = getter.apply(map);
-      return true;
-    } else {
-      current = null;
-      return false;
-    }
-  }
-
-  public void reset() {
-    throw new UnsupportedOperationException();
-  }
-
-  public void close() {
-    // nothing to do
-  }
-
-  private static Function1<SearchHit, Map> mapGetter() {
-    return searchHitFields -> (Map) searchHitFields.fields();
-  }
-
-  private static Function1<SearchHit, Object> singletonGetter(final String fieldName,
-      final Class fieldClass) {
-    return searchHitFields -> {
-      if (searchHitFields.fields().isEmpty()) {
-        return convert(searchHitFields.getSource(), fieldClass);
-      } else {
-        return convert(searchHitFields.getFields(), fieldClass);
-      }
-    };
-  }
-
-  /**
-   * Function that extracts a given set of fields from {@link SearchHit}
-   * objects.
-   *
-   * @param fields List of fields to project
-   *
-   * @return function that converts the search result into a generic array
-   */
-  private static Function1<SearchHit, Object[]> listGetter(
-      final List<Map.Entry<String, Class>> fields) {
-    return 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 (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 {
-          throw new IllegalStateException(
-              String.format(Locale.ROOT, "No result for %s", field));
-        }
-      }
-      return objects;
-    };
-  }
-
-  static Function1<SearchHit, Object> getter(List<Map.Entry<String, Class>> fields) {
-    //noinspection unchecked
-    return fields == null
-      ? (Function1) mapGetter()
-      : fields.size() == 1
-      ? singletonGetter(fields.get(0).getKey(), fields.get(0).getValue())
-      : (Function1) listGetter(fields);
-  }
-
-  private static Object convert(Object o, Class clazz) {
-    if (o == null) {
-      return null;
-    }
-    Primitive primitive = Primitive.of(clazz);
-    if (primitive != null) {
-      clazz = primitive.boxClass;
-    } else {
-      primitive = Primitive.ofBox(clazz);
-    }
-    if (clazz.isInstance(o)) {
-      return o;
-    }
-    if (o instanceof Date && primitive != null) {
-      o = ((Date) o).getTime() / DateTimeUtils.MILLIS_PER_DAY;
-    }
-    if (o instanceof Number && primitive != null) {
-      return primitive.number((Number) o);
-    }
-    return o;
-  }
-}
-
-// End Elasticsearch2Enumerator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 6d464c8..0000000
--- a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Schema.java
+++ /dev/null
@@ -1,149 +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.adapter.elasticsearch2;
-
-import org.apache.calcite.adapter.elasticsearch.ElasticsearchSchema;
-import org.apache.calcite.schema.Table;
-import org.apache.calcite.schema.impl.AbstractSchema;
-
-import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
-import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
-import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
-import org.elasticsearch.client.Client;
-import org.elasticsearch.client.transport.TransportClient;
-import org.elasticsearch.cluster.metadata.MappingMetaData;
-import org.elasticsearch.cluster.node.DiscoveryNode;
-import org.elasticsearch.common.collect.ImmutableOpenMap;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.transport.InetSocketTransportAddress;
-import org.elasticsearch.common.transport.TransportAddress;
-
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Schema mapped onto an index of ELASTICSEARCH types.
- *
- * <p>Each table in the schema is an ELASTICSEARCH type in that index.
- */
-public class Elasticsearch2Schema extends AbstractSchema
-    implements ElasticsearchSchema {
-  final String index;
-
-  private transient Client client;
-
-  /**
-   * Creates an Elasticsearch2 schema.
-   *
-   * @param coordinates Map of Elasticsearch node locations (host, port)
-   * @param userConfig Map of user-specified configurations
-   * @param indexName Elasticsearch database name, e.g. "usa".
-   */
-  Elasticsearch2Schema(Map<String, Integer> coordinates,
-      Map<String, String> userConfig, String indexName) {
-    super();
-
-    final List<InetSocketAddress> transportAddresses = new ArrayList<>();
-    for (Map.Entry<String, Integer> coordinate: coordinates.entrySet()) {
-      transportAddresses.add(
-          new InetSocketAddress(coordinate.getKey(), coordinate.getValue()));
-    }
-
-    open(transportAddresses, userConfig);
-
-    if (client != null) {
-      final String[] indices = client.admin().indices()
-          .getIndex(new GetIndexRequest().indices(indexName))
-          .actionGet().getIndices();
-      if (indices.length == 1) {
-        index = indices[0];
-      } else {
-        index = null;
-      }
-    } else {
-      index = null;
-    }
-  }
-
-  /**
-   * Allows schema to be instantiated from existing elastic search client.
-   * This constructor is used in tests.
-   *
-   * @param client Existing (injectable) client instance
-   * @param index Index name
-   */
-  @VisibleForTesting
-  Elasticsearch2Schema(Client client, String index) {
-    this.client = Objects.requireNonNull(client, "client");
-    this.index = Objects.requireNonNull(index, "index");
-  }
-
-  @Override protected Map<String, Table> getTableMap() {
-    final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
-
-    try {
-      GetMappingsResponse response = client.admin().indices()
-          .getMappings(new GetMappingsRequest().indices(index))
-          .get();
-      ImmutableOpenMap<String, MappingMetaData> mapping = response.getMappings().get(index);
-      for (ObjectObjectCursor<String, MappingMetaData> c: mapping) {
-        builder.put(c.key, new Elasticsearch2Table(client, index, c.key));
-      }
-    } catch (RuntimeException e) {
-      throw e;
-    } catch (Exception e) {
-      throw new RuntimeException(e);
-    }
-    return builder.build();
-  }
-
-  private void open(List<InetSocketAddress> transportAddresses, Map<String, String> userConfig) {
-    final List<TransportAddress> transportNodes = new ArrayList<>(transportAddresses.size());
-    for (InetSocketAddress address : transportAddresses) {
-      transportNodes.add(new InetSocketTransportAddress(address));
-    }
-
-    Settings settings = Settings.settingsBuilder().put(userConfig).build();
-
-    final TransportClient transportClient = TransportClient.builder().settings(settings).build();
-    for (TransportAddress transport : transportNodes) {
-      transportClient.addTransportAddress(transport);
-    }
-
-    final List<DiscoveryNode> nodes = ImmutableList.copyOf(transportClient.connectedNodes());
-    if (nodes.isEmpty()) {
-      throw new IllegalStateException("Cannot connect to any elasticsearch node: "
-              + transportNodes);
-    }
-
-    client = transportClient;
-  }
-
-  @Override public String getIndex() {
-    return index;
-  }
-}
-
-// End Elasticsearch2Schema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2SchemaFactory.java
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2SchemaFactory.java b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2SchemaFactory.java
deleted file mode 100644
index 19378a0..0000000
--- a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2SchemaFactory.java
+++ /dev/null
@@ -1,63 +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.adapter.elasticsearch2;
-
-import org.apache.calcite.schema.Schema;
-import org.apache.calcite.schema.SchemaFactory;
-import org.apache.calcite.schema.SchemaPlus;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * Factory that creates an {@link Elasticsearch2Schema}.
- *
- * <p>Allows a custom schema to be included in a model.json file.
- */
-@SuppressWarnings("UnusedDeclaration")
-public class Elasticsearch2SchemaFactory implements SchemaFactory {
-
-  public Elasticsearch2SchemaFactory() {
-  }
-
-  @Override public Schema create(SchemaPlus parentSchema, String name,
-      Map<String, Object> operand) {
-    final Map map = (Map) operand;
-
-    final ObjectMapper mapper = new ObjectMapper();
-    mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
-
-    try {
-      final Map<String, Integer> coordinates =
-          mapper.readValue((String) map.get("coordinates"),
-              new TypeReference<Map<String, Integer>>() { });
-      final Map<String, String> userConfig =
-          mapper.readValue((String) map.get("userConfig"),
-              new TypeReference<Map<String, String>>() { });
-      final String index = (String) map.get("index");
-      return new Elasticsearch2Schema(coordinates, userConfig, index);
-    } catch (IOException e) {
-      throw new RuntimeException("Cannot parse values from json", e);
-    }
-  }
-}
-
-// End Elasticsearch2SchemaFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 8bfd8cf..0000000
--- a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Table.java
+++ /dev/null
@@ -1,77 +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.adapter.elasticsearch2;
-
-import org.apache.calcite.adapter.elasticsearch.AbstractElasticsearchTable;
-import org.apache.calcite.linq4j.AbstractEnumerable;
-import org.apache.calcite.linq4j.Enumerable;
-import org.apache.calcite.linq4j.Enumerator;
-import org.apache.calcite.linq4j.function.Function1;
-
-import org.apache.calcite.util.Util;
-
-import org.elasticsearch.client.Client;
-import org.elasticsearch.search.SearchHit;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Table based on an Elasticsearch2 type.
- */
-public class Elasticsearch2Table extends AbstractElasticsearchTable {
-  private final Client client;
-
-  /**
-   * Creates an Elasticsearch2Table.
-   *
-   * @param client Existing ES instance
-   * @param indexName Name of the index
-   * @param typeName Index type
-   */
-  Elasticsearch2Table(Client client, String indexName, String typeName) {
-    super(indexName, typeName);
-    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) {
-    final String dbName = index;
-
-    final String queryString = "{" + Util.toString(ops, "", ", ", "") + "}";
-
-    final Function1<SearchHit, Object> getter = Elasticsearch2Enumerator.getter(fields);
-
-    return new AbstractEnumerable<Object>() {
-      public Enumerator<Object> enumerator() {
-        final Iterator<SearchHit> cursor = client.prepareSearch(dbName).setTypes(typeName)
-            .setSource(queryString).execute().actionGet().getHits().iterator();
-        return new Elasticsearch2Enumerator(cursor, getter);
-      }
-    };
-  }
-}
-
-// End Elasticsearch2Table.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/package-info.java
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/package-info.java b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/package-info.java
deleted file mode 100644
index a1c10c9..0000000
--- a/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/package-info.java
+++ /dev/null
@@ -1,26 +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.
- */
-
-/**
- * Query provider based on an Elasticsearch2 DB.
- */
-@PackageMarker
-package org.apache.calcite.adapter.elasticsearch2;
-
-import org.apache.calcite.avatica.util.PackageMarker;
-
-// End package-info.java


[5/5] calcite git commit: [CALCITE-2376] Unify ES2 and ES5 adapters. Migrate to low-level ES rest client as main transport. (Andrei Sereda)

Posted by mm...@apache.org.
[CALCITE-2376] Unify ES2 and ES5 adapters. Migrate to low-level ES rest client as main transport. (Andrei Sereda)

== Major change to elastic search adapter(s)

1. Combine ES2 and ES5 under single maven module (called elasticsearch). Move shared ES source from core to
separate elasticsearch module.

2. Migrate transport layer to low-level rest client. There are no more heavy core server-side ES dependencies
(like Lucene, Netty etc.) in adapter, just apache http client and apache codecs.
Native Transport client will be removed by the vendor in the next release (ES7-8).

3. Manually parse search results using jackson JSON library (this is already a transitive dependency from core)

== Expected breaking changes for clients:
1. Change class names of Schema and SchemaFactory. They don't contain ES major version number anymore (2, 5, 6 etc.):
   - ElasticsearchSchemaFactory(new) vs Elasticsearch{2|5}SchemaFactory (old)
   - ElasticsearchSchema(new) vs Elasticsearch{2|5}Schema (old)
   - changed package name(s)
This affects existing configurations (especially JSON files)

2. There is a single maven module elasticsearch. elasticsearch2 and elasticsearch5 are removed.
Client will have to manually update their artifact dependencies.


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/98838180
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/98838180
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/98838180

Branch: refs/heads/master
Commit: 988381807a16226434bcd0d126e7f88b351f4341
Parents: 195414d
Author: Andrei Sereda <an...@nospam.com>
Authored: Tue Jun 26 21:11:06 2018 -0400
Committer: Michael Mior <mm...@uwaterloo.ca>
Committed: Mon Jul 9 12:14:26 2018 -0400

----------------------------------------------------------------------
 .../AbstractElasticsearchTable.java             | 140 -------
 .../elasticsearch/ElasticsearchFilter.java      | 294 --------------
 .../elasticsearch/ElasticsearchMethod.java      |  50 ---
 .../elasticsearch/ElasticsearchProject.java     | 115 ------
 .../adapter/elasticsearch/ElasticsearchRel.java |  58 ---
 .../elasticsearch/ElasticsearchRules.java       | 238 -----------
 .../elasticsearch/ElasticsearchSchema.java      |  33 --
 .../elasticsearch/ElasticsearchSort.java        | 104 -----
 .../elasticsearch/ElasticsearchTableScan.java   |  89 ----
 .../ElasticsearchToEnumerableConverter.java     | 115 ------
 .../ElasticsearchToEnumerableConverterRule.java |  54 ---
 .../MapProjectionFieldVisitor.java              |  42 --
 .../adapter/elasticsearch/package-info.java     |  26 --
 elasticsearch/pom.xml                           | 187 +++++++++
 .../AbstractElasticsearchTable.java             | 145 +++++++
 .../elasticsearch/ElasticsearchEnumerators.java | 111 +++++
 .../elasticsearch/ElasticsearchFilter.java      | 305 ++++++++++++++
 .../elasticsearch/ElasticsearchMethod.java      |  50 +++
 .../elasticsearch/ElasticsearchProject.java     | 115 ++++++
 .../adapter/elasticsearch/ElasticsearchRel.java |  58 +++
 .../elasticsearch/ElasticsearchRules.java       | 240 +++++++++++
 .../elasticsearch/ElasticsearchSchema.java      | 114 ++++++
 .../ElasticsearchSchemaFactory.java             |  95 +++++
 .../ElasticsearchSearchResult.java              | 173 ++++++++
 .../elasticsearch/ElasticsearchSort.java        | 104 +++++
 .../elasticsearch/ElasticsearchTable.java       | 135 +++++++
 .../elasticsearch/ElasticsearchTableScan.java   |  89 ++++
 .../ElasticsearchToEnumerableConverter.java     | 124 ++++++
 .../ElasticsearchToEnumerableConverterRule.java |  54 +++
 .../elasticsearch/ElasticsearchVersion.java     |  58 +++
 .../MapProjectionFieldVisitor.java              |  43 ++
 .../adapter/elasticsearch/package-info.java     |  26 ++
 .../elasticsearch/ElasticSearchAdapterTest.java | 400 ++++++++++++++++++
 .../elasticsearch/ElasticsearchVersionTest.java |  65 +++
 .../EmbeddedElasticsearchNode.java              | 157 +++++++
 .../EmbeddedElasticsearchPolicy.java            | 119 ++++++
 .../calcite/test/ElasticsearchChecker.java      |  44 ++
 elasticsearch/src/test/resources/log4j2.xml     |  36 ++
 elasticsearch/src/test/resources/zips-mini.json | 149 +++++++
 elasticsearch2/pom.xml                          | 150 -------
 .../Elasticsearch2Enumerator.java               | 150 -------
 .../elasticsearch2/Elasticsearch2Schema.java    | 149 -------
 .../Elasticsearch2SchemaFactory.java            |  63 ---
 .../elasticsearch2/Elasticsearch2Table.java     |  77 ----
 .../adapter/elasticsearch2/package-info.java    |  26 --
 .../Elasticsearch2AdapterTest.java              | 398 ------------------
 .../EmbeddedElasticsearchNode.java              | 155 -------
 .../EmbeddedElasticsearchPolicy.java            | 102 -----
 .../calcite/test/Elasticsearch2AdapterIT.java   | 265 ------------
 .../calcite/test/ElasticsearchChecker.java      |  45 ---
 .../src/test/resources/log4j.properties         |  24 --
 .../src/test/resources/zips-mini.json           | 149 -------
 elasticsearch5/pom.xml                          | 180 ---------
 .../Elasticsearch5Enumerator.java               | 147 -------
 .../elasticsearch5/Elasticsearch5Schema.java    | 154 -------
 .../Elasticsearch5SchemaFactory.java            |  63 ---
 .../elasticsearch5/Elasticsearch5Table.java     |  96 -----
 .../adapter/elasticsearch5/package-info.java    |  26 --
 .../Elasticsearch5AdapterTest.java              | 405 -------------------
 .../EmbeddedElasticsearchNode.java              | 162 --------
 .../EmbeddedElasticsearchPolicy.java            | 102 -----
 .../calcite/test/Elasticsearch5AdapterIT.java   | 265 ------------
 .../calcite/test/ElasticsearchChecker.java      |  45 ---
 .../src/test/resources/log4j.properties         |  24 --
 elasticsearch5/src/test/resources/log4j2.xml    |  32 --
 .../src/test/resources/zips-mini.json           | 149 -------
 pom.xml                                         |  21 +-
 67 files changed, 3200 insertions(+), 4978 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 8cc5933..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java
+++ /dev/null
@@ -1,140 +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.adapter.elasticsearch;
-
-import org.apache.calcite.adapter.java.AbstractQueryableTable;
-import org.apache.calcite.linq4j.Enumerable;
-import org.apache.calcite.linq4j.Enumerator;
-import org.apache.calcite.linq4j.QueryProvider;
-import org.apache.calcite.linq4j.Queryable;
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.schema.SchemaPlus;
-import org.apache.calcite.schema.TranslatableTable;
-import org.apache.calcite.schema.impl.AbstractTableQueryable;
-import org.apache.calcite.sql.type.SqlTypeName;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Table based on an Elasticsearch type.
- */
-public abstract class AbstractElasticsearchTable extends AbstractQueryableTable
-    implements TranslatableTable {
-  protected final String indexName;
-  protected final String typeName;
-
-  /**
-   * Creates an ElasticsearchTable.
-   */
-  public AbstractElasticsearchTable(String indexName, String typeName) {
-    super(Object[].class);
-    this.indexName = indexName;
-    this.typeName = typeName;
-  }
-
-  @Override public String toString() {
-    return "ElasticsearchTable{" + typeName + "}";
-  }
-
-  public RelDataType getRowType(RelDataTypeFactory relDataTypeFactory) {
-    final RelDataType mapType = relDataTypeFactory.createMapType(
-        relDataTypeFactory.createSqlType(SqlTypeName.VARCHAR),
-        relDataTypeFactory.createTypeWithNullability(
-            relDataTypeFactory.createSqlType(SqlTypeName.ANY),
-            true));
-    return relDataTypeFactory.builder().add("_MAP", mapType).build();
-  }
-
-  public <T> Queryable<T> asQueryable(QueryProvider queryProvider, SchemaPlus schema,
-      String tableName) {
-    return new ElasticsearchQueryable<>(queryProvider, schema, this, tableName);
-  }
-
-  public RelNode toRel(RelOptTable.ToRelContext context, RelOptTable relOptTable) {
-    final RelOptCluster cluster = context.getCluster();
-    return new ElasticsearchTableScan(cluster, cluster.traitSetOf(ElasticsearchRel.CONVENTION),
-        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,
-   * <code>client.prepareSearch(index).setTypes(type)
-   * .setSource("{\"fields\" : [\"state\"]}")</code></p>
-   *
-   * @param index Elasticsearch index
-   * @param ops List of operations represented as Json strings.
-   * @param fields List of fields to project; or null to return map
-   * @return Enumerator of results
-   */
-  protected abstract Enumerable<Object> find(String index, List<String> ops,
-      List<Map.Entry<String, Class>> fields);
-
-  /**
-   * Implementation of {@link Queryable} based on
-   * a {@link AbstractElasticsearchTable}.
-   *
-   * @param <T> element type
-   */
-  public static class ElasticsearchQueryable<T> extends AbstractTableQueryable<T> {
-    public ElasticsearchQueryable(QueryProvider queryProvider, SchemaPlus schema,
-        AbstractElasticsearchTable table, String tableName) {
-      super(queryProvider, schema, table, tableName);
-    }
-
-    public Enumerator<T> enumerator() {
-      return null;
-    }
-
-    private String getIndex() {
-      return schema.unwrap(ElasticsearchSchema.class).getIndex();
-    }
-
-    private AbstractElasticsearchTable getTable() {
-      return (AbstractElasticsearchTable) table;
-    }
-
-    /** Called via code-generation.
-     *
-     * @see ElasticsearchMethod#ELASTICSEARCH_QUERYABLE_FIND
-     */
-    @SuppressWarnings("UnusedDeclaration")
-    public Enumerable<Object> find(List<String> ops,
-        List<Map.Entry<String, Class>> fields) {
-      return getTable().find(getIndex(), ops, fields);
-    }
-  }
-}
-
-// End AbstractElasticsearchTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
deleted file mode 100644
index 06b39c0..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
+++ /dev/null
@@ -1,294 +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.adapter.elasticsearch;
-
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptCost;
-import org.apache.calcite.plan.RelOptPlanner;
-import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.Filter;
-import org.apache.calcite.rel.core.Project;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rex.RexCall;
-import org.apache.calcite.rex.RexInputRef;
-import org.apache.calcite.rex.RexLiteral;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.util.JsonBuilder;
-import org.apache.calcite.util.Pair;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Implementation of a {@link org.apache.calcite.rel.core.Filter}
- * relational expression in Elasticsearch.
- */
-public class ElasticsearchFilter extends Filter implements ElasticsearchRel {
-  public ElasticsearchFilter(RelOptCluster cluster, RelTraitSet traitSet, RelNode child,
-      RexNode condition) {
-    super(cluster, traitSet, child, condition);
-    assert getConvention() == ElasticsearchRel.CONVENTION;
-    assert getConvention() == child.getConvention();
-  }
-
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
-    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
-  }
-
-  @Override public Filter copy(RelTraitSet relTraitSet, RelNode input, RexNode condition) {
-    return new ElasticsearchFilter(getCluster(), relTraitSet, input, condition);
-  }
-
-  @Override public void implement(Implementor implementor) {
-    implementor.visitChild(0, getInput());
-    List<String> fieldNames;
-    if (input instanceof Project) {
-      final List<RexNode> projects = ((Project) input).getProjects();
-      fieldNames = new ArrayList<>(projects.size());
-      for (RexNode project : projects) {
-        String name = project.accept(MapProjectionFieldVisitor.INSTANCE);
-        fieldNames.add(name);
-      }
-    } else {
-      fieldNames = ElasticsearchRules.elasticsearchFieldNames(getRowType());
-    }
-    Translator translator = new Translator(fieldNames);
-    String match = translator.translateMatch(condition);
-    implementor.add(match);
-  }
-
-  /**
-   * Translates {@link RexNode} expressions into Elasticsearch expression strings.
-   */
-  static class Translator {
-    final JsonBuilder builder = new JsonBuilder();
-    final Multimap<String, Pair<String, RexLiteral>> multimap =
-        HashMultimap.create();
-    final Map<String, RexLiteral> eqMap = new LinkedHashMap<>();
-    private final List<String> fieldNames;
-
-    Translator(List<String> fieldNames) {
-      this.fieldNames = fieldNames;
-    }
-
-    private String translateMatch(RexNode condition) {
-      // filter node
-      final Map<String, Object> filterMap = new LinkedHashMap<>();
-      filterMap.put("filter", translateOr(condition));
-
-      // constant_score node
-      final Map<String, Object> map = builder.map();
-      map.put("constant_score", filterMap);
-
-      return "\"query\" : " + builder.toJsonString(map).replaceAll("\\s+", "");
-    }
-
-    private Object translateOr(RexNode condition) {
-      final List<Object> list = new ArrayList<>();
-
-      final List<RexNode> orNodes = RelOptUtil.disjunctions(condition);
-      for (RexNode node : orNodes) {
-        List<Map<String, Object>> andNodes = translateAnd(node);
-
-        if (andNodes.size() > 0) {
-          Map<String, Object> andClause = new HashMap<>();
-          andClause.put("must", andNodes);
-
-          // boolean filters
-          LinkedHashMap<String, Object> filterEvaluator = new LinkedHashMap<>();
-          filterEvaluator.put("bool", andClause);
-          list.add(filterEvaluator);
-        } else {
-          list.add(andNodes.get(0));
-        }
-      }
-
-      if (orNodes.size() > 1) {
-        Map<String, Object> map = builder.map();
-        map.put("should", list);
-
-        // boolean filters
-        LinkedHashMap<String, Object> filterEvaluator = new LinkedHashMap<>();
-        filterEvaluator.put("bool", map);
-        return filterEvaluator;
-      } else {
-        return list.get(0);
-      }
-    }
-
-    private void addPredicate(Map<String, Object> map, String op, Object v) {
-      if (map.containsKey(op) && stronger(op, map.get(op), v)) {
-        return;
-      }
-      map.put(op, v);
-    }
-
-    /**
-     * Translates a condition that may be an AND of other conditions. Gathers
-     * together conditions that apply to the same field.
-     */
-    private List<Map<String, Object>> translateAnd(RexNode node0) {
-      eqMap.clear();
-      multimap.clear();
-      for (RexNode node : RelOptUtil.conjunctions(node0)) {
-        translateMatch2(node);
-      }
-      List<Map<String, Object>> filters = new ArrayList<>();
-      for (Map.Entry<String, RexLiteral> entry : eqMap.entrySet()) {
-        multimap.removeAll(entry.getKey());
-
-        Map<String, Object> filter = new HashMap<>();
-        filter.put(entry.getKey(), literalValue(entry.getValue()));
-
-        Map<String, Object> map = new HashMap<>();
-        map.put("term", filter);
-        filters.add(map);
-      }
-      for (Map.Entry<String, Collection<Pair<String, RexLiteral>>> entry
-          : multimap.asMap().entrySet()) {
-        Map<String, Object> map2 = builder.map();
-
-        Map<String, Object> map = new HashMap<>();
-        for (Pair<String, RexLiteral> s : entry.getValue()) {
-          if (!s.left.equals("not")) {
-            addPredicate(map2, s.left, literalValue(s.right));
-
-            Map<String, Object> filter = new HashMap<>();
-            filter.put(entry.getKey(), map2);
-
-            map.put("range", filter);
-          } else {
-            map2.put(entry.getKey(), literalValue(s.right));
-
-            Map<String, Object> termMap = new HashMap<>();
-            termMap.put("term", map2);
-
-            map.put("not", termMap);
-          }
-        }
-        filters.add(map);
-      }
-      return filters;
-    }
-
-    private boolean stronger(String key, Object v0, Object v1) {
-      if (key.equals("lt") || key.equals("lte")) {
-        if (v0 instanceof Number && v1 instanceof Number) {
-          return ((Number) v0).doubleValue() < ((Number) v1).doubleValue();
-        }
-        if (v0 instanceof String && v1 instanceof String) {
-          return v0.toString().compareTo(v1.toString()) < 0;
-        }
-      }
-      if (key.equals("gt") || key.equals("gte")) {
-        return stronger("lt", v1, v0);
-      }
-      return false;
-    }
-
-    private static Object literalValue(RexLiteral literal) {
-      return literal.getValue2();
-    }
-
-    private Void translateMatch2(RexNode node) {
-      switch (node.getKind()) {
-      case EQUALS:
-        return translateBinary(null, null, (RexCall) node);
-      case LESS_THAN:
-        return translateBinary("lt", "gt", (RexCall) node);
-      case LESS_THAN_OR_EQUAL:
-        return translateBinary("lte", "gte", (RexCall) node);
-      case NOT_EQUALS:
-        return translateBinary("not", "not", (RexCall) node);
-      case GREATER_THAN:
-        return translateBinary("gt", "lt", (RexCall) node);
-      case GREATER_THAN_OR_EQUAL:
-        return translateBinary("gte", "lte", (RexCall) node);
-      default:
-        throw new AssertionError("cannot translate " + node);
-      }
-    }
-
-    /**
-     * Translates a call to a binary operator, reversing arguments if
-     * necessary.
-     */
-    private Void translateBinary(String op, String rop, RexCall call) {
-      final RexNode left = call.operands.get(0);
-      final RexNode right = call.operands.get(1);
-      boolean b = translateBinary2(op, left, right);
-      if (b) {
-        return null;
-      }
-      b = translateBinary2(rop, right, left);
-      if (b) {
-        return null;
-      }
-      throw new AssertionError("cannot translate op " + op + " call " + call);
-    }
-
-    /**
-     * Translates a call to a binary operator. Returns whether successful.
-     */
-    private boolean translateBinary2(String op, RexNode left, RexNode right) {
-      switch (right.getKind()) {
-      case LITERAL:
-        break;
-      default:
-        return false;
-      }
-      final RexLiteral rightLiteral = (RexLiteral) right;
-      switch (left.getKind()) {
-      case INPUT_REF:
-        final RexInputRef left1 = (RexInputRef) left;
-        String name = fieldNames.get(left1.getIndex());
-        translateOp2(op, name, rightLiteral);
-        return true;
-      case CAST:
-        return translateBinary2(op, ((RexCall) left).operands.get(0), right);
-      case OTHER_FUNCTION:
-        String itemName = ElasticsearchRules.isItem((RexCall) left);
-        if (itemName != null) {
-          translateOp2(op, itemName, rightLiteral);
-          return true;
-        }
-        // fall through
-      default:
-        return false;
-      }
-    }
-
-    private void translateOp2(String op, String name, RexLiteral right) {
-      if (op == null) {
-        eqMap.put(name, right);
-      } else {
-        multimap.put(name, Pair.of(op, right));
-      }
-    }
-  }
-}
-
-// End ElasticsearchFilter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
deleted file mode 100644
index 72753e6..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
+++ /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.
- */
-package org.apache.calcite.adapter.elasticsearch;
-
-import org.apache.calcite.linq4j.tree.Types;
-
-import com.google.common.collect.ImmutableMap;
-
-import java.lang.reflect.Method;
-import java.util.List;
-
-/**
- * Builtin methods in the Elasticsearch adapter.
- */
-enum ElasticsearchMethod {
-  ELASTICSEARCH_QUERYABLE_FIND(AbstractElasticsearchTable.ElasticsearchQueryable.class,
-      "find", List.class, List.class);
-
-  public final Method method;
-
-  public static final ImmutableMap<Method, ElasticsearchMethod> MAP;
-
-  static {
-    final ImmutableMap.Builder<Method, ElasticsearchMethod> builder = ImmutableMap.builder();
-    for (ElasticsearchMethod value: ElasticsearchMethod.values()) {
-      builder.put(value.method, value);
-    }
-    MAP = builder.build();
-  }
-
-  ElasticsearchMethod(Class clazz, String methodName, Class... argumentTypes) {
-    this.method = Types.lookupMethod(clazz, methodName, argumentTypes);
-  }
-}
-
-// End ElasticsearchMethod.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/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
deleted file mode 100644
index 63270db..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java
+++ /dev/null
@@ -1,115 +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.adapter.elasticsearch;
-
-import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptCost;
-import org.apache.calcite.plan.RelOptPlanner;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.Project;
-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 com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Implementation of {@link org.apache.calcite.rel.core.Project}
- * relational expression in Elasticsearch.
- */
-public class ElasticsearchProject extends Project implements ElasticsearchRel {
-  public ElasticsearchProject(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
-      List<? extends RexNode> projects, RelDataType rowType) {
-    super(cluster, traitSet, input, projects, rowType);
-    assert getConvention() == ElasticsearchRel.CONVENTION;
-    assert getConvention() == input.getConvention();
-  }
-
-  @Override public Project copy(RelTraitSet relTraitSet, RelNode input, List<RexNode> projects,
-      RelDataType relDataType) {
-    return new ElasticsearchProject(getCluster(), traitSet, input, projects, relDataType);
-  }
-
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
-    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
-  }
-
-  @Override public void implement(Implementor implementor) {
-    implementor.visitChild(0, getInput());
-
-    final List<String> inFields =
-            ElasticsearchRules.elasticsearchFieldNames(getInput().getRowType());
-    final ElasticsearchRules.RexToElasticsearchTranslator translator =
-            new ElasticsearchRules.RexToElasticsearchTranslator(
-                    (JavaTypeFactory) getCluster().getTypeFactory(), inFields);
-
-    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 + "\"")) {
-        fields.add(name);
-      } else if (expr.matches("\"literal\":.+")) {
-        scriptFields.add(ElasticsearchRules.quote(name)
-                + ":{\"script\": "
-                + expr.split(":")[1] + "}");
-      } else {
-        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, ElasticsearchRules::quote);
-
-      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\": {")
-          .append(String.join(", ", scriptFields))
-          .append("}");
-    }
-
-    for (String opfield : implementor.list) {
-      if (opfield.startsWith("\"_source\"")) {
-        implementor.list.remove(opfield);
-      }
-    }
-    implementor.add(query.toString());
-  }
-}
-
-// End ElasticsearchProject.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
deleted file mode 100644
index 436adf9..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
+++ /dev/null
@@ -1,58 +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.adapter.elasticsearch;
-
-import org.apache.calcite.plan.Convention;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.rel.RelNode;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Relational expression that uses Elasticsearch calling convention.
- */
-public interface ElasticsearchRel extends RelNode {
-  void implement(Implementor implementor);
-
-  /**
-   * Calling convention for relational operations that occur in Elasticsearch.
-   */
-  Convention CONVENTION = new Convention.Impl("ELASTICSEARCH", ElasticsearchRel.class);
-
-  /**
-   * Callback for the implementation process that converts a tree of
-   * {@link ElasticsearchRel} nodes into an Elasticsearch query.
-   */
-  class Implementor {
-    final List<String> list = new ArrayList<>();
-
-    RelOptTable table;
-    AbstractElasticsearchTable elasticsearchTable;
-
-    public void add(String findOp) {
-      list.add(findOp);
-    }
-
-    public void visitChild(int ordinal, RelNode input) {
-      assert ordinal == 0;
-      ((ElasticsearchRel) input).implement(this);
-    }
-  }
-}
-
-// End ElasticsearchRel.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
deleted file mode 100644
index c4f6728..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
+++ /dev/null
@@ -1,238 +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.adapter.elasticsearch;
-
-import org.apache.calcite.adapter.enumerable.RexImpTable;
-import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
-import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.plan.Convention;
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelTrait;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelCollations;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.convert.ConverterRule;
-import org.apache.calcite.rel.core.Sort;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.logical.LogicalProject;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rex.RexCall;
-import org.apache.calcite.rex.RexInputRef;
-import org.apache.calcite.rex.RexLiteral;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexVisitorImpl;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.sql.validate.SqlValidatorUtil;
-
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Rules and relational operators for
- * {@link ElasticsearchRel#CONVENTION ELASTICSEARCH}
- * calling convention.
- */
-class ElasticsearchRules {
-  static final RelOptRule[] RULES = {
-      ElasticsearchSortRule.INSTANCE,
-      ElasticsearchFilterRule.INSTANCE,
-      ElasticsearchProjectRule.INSTANCE
-  };
-
-  private ElasticsearchRules() {}
-
-  /**
-   * Returns 'string' if it is a call to item['string'], null otherwise.
-   */
-  static String isItem(RexCall call) {
-    if (call.getOperator() != SqlStdOperatorTable.ITEM) {
-      return null;
-    }
-    final RexNode op0 = call.getOperands().get(0);
-    final RexNode op1 = call.getOperands().get(1);
-
-    if (op0 instanceof RexInputRef
-        && ((RexInputRef) op0).getIndex() == 0
-        && op1 instanceof RexLiteral
-        && ((RexLiteral) op1).getValue2() instanceof String) {
-      return (String) ((RexLiteral) op1).getValue2();
-    }
-    return null;
-  }
-
-  static List<String> elasticsearchFieldNames(final RelDataType rowType) {
-    return SqlValidatorUtil.uniquify(
-        new AbstractList<String>() {
-          @Override public String get(int index) {
-            final String name = rowType.getFieldList().get(index).getName();
-            return name.startsWith("$") ? "_" + name.substring(2) : name;
-          }
-
-          @Override public int size() {
-            return rowType.getFieldCount();
-          }
-        },
-        SqlValidatorUtil.EXPR_SUGGESTER, true);
-  }
-
-  static String quote(String s) {
-    return "\"" + s + "\"";
-  }
-
-  /**
-   * Translator from {@link RexNode} to strings in Elasticsearch's expression
-   * language.
-   */
-  static class RexToElasticsearchTranslator extends RexVisitorImpl<String> {
-    private final JavaTypeFactory typeFactory;
-    private final List<String> inFields;
-
-    RexToElasticsearchTranslator(JavaTypeFactory typeFactory, List<String> inFields) {
-      super(true);
-      this.typeFactory = typeFactory;
-      this.inFields = inFields;
-    }
-
-    @Override public String visitLiteral(RexLiteral literal) {
-      if (literal.getValue() == null) {
-        return "null";
-      }
-      return "\"literal\":\""
-        + RexToLixTranslator.translateLiteral(literal, literal.getType(),
-          typeFactory, RexImpTable.NullAs.NOT_POSSIBLE)
-        + "\"";
-    }
-
-    @Override public String visitInputRef(RexInputRef inputRef) {
-      return quote(inFields.get(inputRef.getIndex()));
-    }
-
-    @Override public String visitCall(RexCall call) {
-      final String name = isItem(call);
-      if (name != null) {
-        return "\"" + name + "\"";
-      }
-
-      final List<String> strings = visitList(call.operands);
-      if (call.getKind() == SqlKind.CAST) {
-        return strings.get(0).startsWith("$") ? strings.get(0).substring(1) : strings.get(0);
-      }
-      if (call.getOperator() == SqlStdOperatorTable.ITEM) {
-        final RexNode op1 = call.getOperands().get(1);
-        if (op1 instanceof RexLiteral && op1.getType().getSqlTypeName() == SqlTypeName.INTEGER) {
-          return stripQuotes(strings.get(0)) + "[" + ((RexLiteral) op1).getValue2() + "]";
-        }
-      }
-      throw new IllegalArgumentException("Translation of " + call.toString()
-        + "is not supported by ElasticsearchProject");
-    }
-
-    private String stripQuotes(String s) {
-      return s.startsWith("'") && s.endsWith("'") ? s.substring(1, s.length() - 1) : s;
-    }
-
-    List<String> visitList(List<RexNode> list) {
-      final List<String> strings = new ArrayList<>();
-      for (RexNode node: list) {
-        strings.add(node.accept(this));
-      }
-      return strings;
-    }
-  }
-
-  /**
-   * Base class for planner rules that convert a relational expression to
-   * Elasticsearch calling convention.
-   */
-  abstract static class ElasticsearchConverterRule extends ConverterRule {
-    final Convention out;
-
-    ElasticsearchConverterRule(Class<? extends RelNode> clazz, RelTrait in, Convention out,
-        String description) {
-      super(clazz, in, out, description);
-      this.out = out;
-    }
-  }
-
-  /**
-   * Rule to convert a {@link org.apache.calcite.rel.core.Sort} to an
-   * {@link ElasticsearchSort}.
-   */
-  private static class ElasticsearchSortRule extends ElasticsearchConverterRule {
-    private static final ElasticsearchSortRule INSTANCE =
-        new ElasticsearchSortRule();
-
-    private ElasticsearchSortRule() {
-      super(Sort.class, Convention.NONE, ElasticsearchRel.CONVENTION,
-          "ElasticsearchSortRule");
-    }
-
-    @Override public RelNode convert(RelNode relNode) {
-      final Sort sort = (Sort) relNode;
-      final RelTraitSet traitSet = sort.getTraitSet().replace(out).replace(sort.getCollation());
-      return new ElasticsearchSort(relNode.getCluster(), traitSet,
-        convert(sort.getInput(), traitSet.replace(RelCollations.EMPTY)), sort.getCollation(),
-        sort.offset, sort.fetch);
-    }
-  }
-
-  /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalFilter} to an
-   * {@link ElasticsearchFilter}.
-   */
-  private static class ElasticsearchFilterRule extends ElasticsearchConverterRule {
-    private static final ElasticsearchFilterRule INSTANCE = new ElasticsearchFilterRule();
-
-    private ElasticsearchFilterRule() {
-      super(LogicalFilter.class, Convention.NONE, ElasticsearchRel.CONVENTION,
-        "ElasticsearchFilterRule");
-    }
-
-    @Override public RelNode convert(RelNode relNode) {
-      final LogicalFilter filter = (LogicalFilter) relNode;
-      final RelTraitSet traitSet = filter.getTraitSet().replace(out);
-      return new ElasticsearchFilter(relNode.getCluster(), traitSet,
-        convert(filter.getInput(), out),
-        filter.getCondition());
-    }
-  }
-
-  /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalProject}
-   * to an {@link ElasticsearchProject}.
-   */
-  private static class ElasticsearchProjectRule extends ElasticsearchConverterRule {
-    private static final ElasticsearchProjectRule INSTANCE = new ElasticsearchProjectRule();
-
-    private ElasticsearchProjectRule() {
-      super(LogicalProject.class, Convention.NONE, ElasticsearchRel.CONVENTION,
-        "ElasticsearchProjectRule");
-    }
-
-    @Override public RelNode convert(RelNode relNode) {
-      final LogicalProject project = (LogicalProject) relNode;
-      final RelTraitSet traitSet = project.getTraitSet().replace(out);
-      return new ElasticsearchProject(project.getCluster(), traitSet,
-        convert(project.getInput(), out), project.getProjects(), project.getRowType());
-    }
-  }
-}
-
-// End ElasticsearchRules.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
deleted file mode 100644
index 7247d19..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
+++ /dev/null
@@ -1,33 +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.adapter.elasticsearch;
-
-import org.apache.calcite.schema.Schema;
-
-/**
- * Gives access to some basic information of the Elasticsearch schema.
- */
-public interface ElasticsearchSchema extends Schema {
-  /**
-   * The name of the Elasticsearch index.
-   *
-   * @return The index name
-   */
-  String getIndex();
-}
-
-// End ElasticsearchSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
deleted file mode 100644
index 851e51b..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
+++ /dev/null
@@ -1,104 +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.adapter.elasticsearch;
-
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptCost;
-import org.apache.calcite.plan.RelOptPlanner;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelCollation;
-import org.apache.calcite.rel.RelFieldCollation;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.Project;
-import org.apache.calcite.rel.core.Sort;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.rex.RexLiteral;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.util.Util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Implementation of {@link org.apache.calcite.rel.core.Sort}
- * relational expression in Elasticsearch.
- */
-public class ElasticsearchSort extends Sort implements ElasticsearchRel {
-  public ElasticsearchSort(RelOptCluster cluster, RelTraitSet traitSet, RelNode child,
-      RelCollation collation, RexNode offset, RexNode fetch) {
-    super(cluster, traitSet, child, collation, offset, fetch);
-    assert getConvention() == ElasticsearchRel.CONVENTION;
-    assert getConvention() == child.getConvention();
-  }
-
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
-    return super.computeSelfCost(planner, mq).multiplyBy(0.05);
-  }
-
-  @Override public Sort copy(RelTraitSet traitSet, RelNode relNode, RelCollation relCollation,
-      RexNode offset, RexNode fetch) {
-    return new ElasticsearchSort(getCluster(), traitSet, relNode, collation, offset, fetch);
-  }
-
-  @Override public void implement(Implementor implementor) {
-    implementor.visitChild(0, getInput());
-    if (!collation.getFieldCollations().isEmpty()) {
-      final List<String> keys = new ArrayList<>();
-      if (input instanceof Project) {
-        final List<RexNode> projects = ((Project) input).getProjects();
-
-        for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
-          RexNode project = projects.get(fieldCollation.getFieldIndex());
-          String name = project.accept(MapProjectionFieldVisitor.INSTANCE);
-          keys.add(ElasticsearchRules.quote(name) + ": " + direction(fieldCollation));
-        }
-      } else {
-        final List<RelDataTypeField> fields = getRowType().getFieldList();
-
-        for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
-          final String name = fields.get(fieldCollation.getFieldIndex()).getName();
-          keys.add(ElasticsearchRules.quote(name) + ": " + direction(fieldCollation));
-        }
-      }
-
-      implementor.add("\"sort\": [ " + Util.toString(keys, "{", "}, {", "}") + "]");
-    }
-
-    if (offset != null) {
-      implementor.add("\"from\": " + ((RexLiteral) offset).getValue());
-    }
-
-    if (fetch != null) {
-      implementor.add("\"size\": " + ((RexLiteral) fetch).getValue());
-    }
-  }
-
-  private String direction(RelFieldCollation fieldCollation) {
-    switch (fieldCollation.getDirection()) {
-    case DESCENDING:
-    case STRICTLY_DESCENDING:
-      return "\"desc\"";
-    case ASCENDING:
-    case STRICTLY_ASCENDING:
-    default:
-      return "\"asc\"";
-    }
-  }
-}
-
-// End ElasticsearchSort.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
deleted file mode 100644
index 1020faa..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
+++ /dev/null
@@ -1,89 +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.adapter.elasticsearch;
-
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptCost;
-import org.apache.calcite.plan.RelOptPlanner;
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.TableScan;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rel.type.RelDataType;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Relational expression representing a scan of an Elasticsearch type.
- *
- * <p> Additional operations might be applied,
- * using the "find" method.</p>
- */
-public class ElasticsearchTableScan extends TableScan implements ElasticsearchRel {
-  private final AbstractElasticsearchTable elasticsearchTable;
-  private final RelDataType projectRowType;
-
-  /**
-   * Creates an ElasticsearchTableScan.
-   *
-   * @param cluster Cluster
-   * @param traitSet Trait set
-   * @param table Table
-   * @param elasticsearchTable Elasticsearch table
-   * @param projectRowType Fields and types to project; null to project raw row
-   */
-  protected ElasticsearchTableScan(RelOptCluster cluster, RelTraitSet traitSet,
-       RelOptTable table, AbstractElasticsearchTable elasticsearchTable,
-       RelDataType projectRowType) {
-    super(cluster, traitSet, table);
-    this.elasticsearchTable = Objects.requireNonNull(elasticsearchTable);
-    this.projectRowType = projectRowType;
-
-    assert getConvention() == ElasticsearchRel.CONVENTION;
-  }
-
-  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
-    assert inputs.isEmpty();
-    return this;
-  }
-
-  @Override public RelDataType deriveRowType() {
-    return projectRowType != null ? projectRowType : super.deriveRowType();
-  }
-
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
-    final float f = projectRowType == null ? 1f : (float) projectRowType.getFieldCount() / 100f;
-    return super.computeSelfCost(planner, mq).multiplyBy(.1 * f);
-  }
-
-  @Override public void register(RelOptPlanner planner) {
-    planner.addRule(ElasticsearchToEnumerableConverterRule.INSTANCE);
-    for (RelOptRule rule: ElasticsearchRules.RULES) {
-      planner.addRule(rule);
-    }
-  }
-
-  @Override public void implement(Implementor implementor) {
-    implementor.elasticsearchTable = elasticsearchTable;
-    implementor.table = table;
-  }
-}
-
-// End ElasticsearchTableScan.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
deleted file mode 100644
index 3cebd3c..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
+++ /dev/null
@@ -1,115 +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.adapter.elasticsearch;
-
-import org.apache.calcite.adapter.enumerable.EnumerableRel;
-import org.apache.calcite.adapter.enumerable.EnumerableRelImplementor;
-import org.apache.calcite.adapter.enumerable.JavaRowFormat;
-import org.apache.calcite.adapter.enumerable.PhysType;
-import org.apache.calcite.adapter.enumerable.PhysTypeImpl;
-import org.apache.calcite.linq4j.tree.BlockBuilder;
-import org.apache.calcite.linq4j.tree.Expression;
-import org.apache.calcite.linq4j.tree.Expressions;
-import org.apache.calcite.linq4j.tree.MethodCallExpression;
-import org.apache.calcite.plan.ConventionTraitDef;
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptCost;
-import org.apache.calcite.plan.RelOptPlanner;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.prepare.CalcitePrepareImpl;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.convert.ConverterImpl;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.runtime.Hook;
-import org.apache.calcite.util.BuiltInMethod;
-import org.apache.calcite.util.Pair;
-
-import com.google.common.collect.Lists;
-
-import java.util.AbstractList;
-import java.util.List;
-
-/**
- * Relational expression representing a scan of a table in an Elasticsearch data source.
- */
-public class ElasticsearchToEnumerableConverter extends ConverterImpl implements EnumerableRel {
-  protected ElasticsearchToEnumerableConverter(RelOptCluster cluster, RelTraitSet traits,
-      RelNode input) {
-    super(cluster, ConventionTraitDef.INSTANCE, traits, input);
-  }
-
-  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
-    return new ElasticsearchToEnumerableConverter(getCluster(), traitSet, sole(inputs));
-  }
-
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
-    return super.computeSelfCost(planner, mq).multiplyBy(.1);
-  }
-
-  @Override public Result implement(EnumerableRelImplementor implementor, Prefer prefer) {
-    final BlockBuilder list = new BlockBuilder();
-    final ElasticsearchRel.Implementor elasticsearchImplementor =
-        new ElasticsearchRel.Implementor();
-    elasticsearchImplementor.visitChild(0, getInput());
-    final RelDataType rowType = getRowType();
-    final PhysType physType = PhysTypeImpl.of(implementor.getTypeFactory(), rowType,
-        prefer.prefer(JavaRowFormat.ARRAY));
-    final Expression fields = list.append("fields",
-        constantArrayList(
-            Pair.zip(ElasticsearchRules.elasticsearchFieldNames(rowType),
-                new AbstractList<Class>() {
-                  @Override public Class get(int index) {
-                    return physType.fieldClass(index);
-                  }
-
-                  @Override public int size() {
-                    return rowType.getFieldCount();
-                  }
-                }),
-            Pair.class));
-    final Expression table = list.append("table",
-        elasticsearchImplementor.table
-            .getExpression(AbstractElasticsearchTable.ElasticsearchQueryable.class));
-    List<String> opList = elasticsearchImplementor.list;
-    final Expression ops = list.append("ops", constantArrayList(opList, String.class));
-    Expression enumerable = list.append("enumerable",
-        Expressions.call(table, ElasticsearchMethod.ELASTICSEARCH_QUERYABLE_FIND.method, ops,
-            fields));
-    if (CalcitePrepareImpl.DEBUG) {
-      System.out.println("Elasticsearch: " + opList);
-    }
-    Hook.QUERY_PLAN.run(opList);
-    list.add(Expressions.return_(null, enumerable));
-    return implementor.result(physType, list.toBlock());
-  }
-
-  /** E.g. {@code constantArrayList("x", "y")} returns
-   * "Arrays.asList('x', 'y')". */
-  private static <T> MethodCallExpression constantArrayList(List<T> values, Class clazz) {
-    return Expressions.call(BuiltInMethod.ARRAYS_AS_LIST.method,
-        Expressions.newArrayInit(clazz, constantList(values)));
-  }
-
-  /** E.g. {@code constantList("x", "y")} returns
-   * {@code {ConstantExpression("x"), ConstantExpression("y")}}. */
-  private static <T> List<Expression> constantList(List<T> values) {
-    return Lists.transform(values, Expressions::constant);
-  }
-}
-
-// End ElasticsearchToEnumerableConverter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
deleted file mode 100644
index 756c078..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
+++ /dev/null
@@ -1,54 +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.adapter.elasticsearch;
-
-import org.apache.calcite.adapter.enumerable.EnumerableConvention;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.convert.ConverterRule;
-import org.apache.calcite.rel.core.RelFactories;
-import org.apache.calcite.tools.RelBuilderFactory;
-
-import java.util.function.Predicate;
-
-/**
- * Rule to convert a relational expression from
- * {@link ElasticsearchRel#CONVENTION} to {@link EnumerableConvention}.
- */
-public class ElasticsearchToEnumerableConverterRule extends ConverterRule {
-  public static final ConverterRule INSTANCE =
-      new ElasticsearchToEnumerableConverterRule(RelFactories.LOGICAL_BUILDER);
-
-  /**
-   * Creates an ElasticsearchToEnumerableConverterRule.
-   *
-   * @param relBuilderFactory Builder for relational expressions
-   */
-  public ElasticsearchToEnumerableConverterRule(
-      RelBuilderFactory relBuilderFactory) {
-    super(RelNode.class, (Predicate<RelNode>) r -> true,
-        ElasticsearchRel.CONVENTION, EnumerableConvention.INSTANCE,
-        relBuilderFactory, "ElasticsearchToEnumerableConverterRule");
-  }
-
-  @Override public RelNode convert(RelNode relNode) {
-    RelTraitSet newTraitSet = relNode.getTraitSet().replace(getOutConvention());
-    return new ElasticsearchToEnumerableConverter(relNode.getCluster(), newTraitSet, relNode);
-  }
-}
-
-// End ElasticsearchToEnumerableConverterRule.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java
deleted file mode 100644
index bcb99a7..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java
+++ /dev/null
@@ -1,42 +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.adapter.elasticsearch;
-
-import org.apache.calcite.rex.RexCall;
-import org.apache.calcite.rex.RexLiteral;
-import org.apache.calcite.rex.RexVisitorImpl;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-
-/**
- * Visitor that extracts the actual field name from an item expression.
- */
-public class MapProjectionFieldVisitor extends RexVisitorImpl<String> {
-  public static final MapProjectionFieldVisitor INSTANCE = new MapProjectionFieldVisitor();
-
-  private MapProjectionFieldVisitor() {
-    super(true);
-  }
-
-  @Override public String visitCall(RexCall call) {
-    if (call.op == SqlStdOperatorTable.ITEM) {
-      return ((RexLiteral) call.getOperands().get(1)).getValueAs(String.class);
-    }
-    return super.visitCall(call);
-  }
-}
-
-// End MapProjectionFieldVisitor.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/core/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java b/core/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
deleted file mode 100644
index 98e1a92..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
+++ /dev/null
@@ -1,26 +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.
- */
-
-/**
- * Base classes for a query provider based on an Elasticsearch DB.
- */
-@PackageMarker
-package org.apache.calcite.adapter.elasticsearch;
-
-import org.apache.calcite.avatica.util.PackageMarker;
-
-// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/pom.xml
----------------------------------------------------------------------
diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml
new file mode 100644
index 0000000..d938da8
--- /dev/null
+++ b/elasticsearch/pom.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.calcite</groupId>
+    <artifactId>calcite</artifactId>
+    <version>1.17.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-elasticsearch</artifactId>
+  <packaging>jar</packaging>
+  <version>1.17.0-SNAPSHOT</version>
+  <name>Calcite Elasticsearch</name>
+  <description>Elasticsearch adapter for Calcite</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-linq4j</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.elasticsearch.client</groupId>
+      <artifactId>elasticsearch-rest-client</artifactId>
+      <version>${elasticsearch.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+      <version>${httpcore.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.elasticsearch</groupId>
+      <artifactId>elasticsearch</artifactId>
+      <version>${elasticsearch.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <!-- Using special netty transport to start embedded ES instance during tests -->
+      <groupId>org.elasticsearch.plugin</groupId>
+      <artifactId>transport-netty4-client</artifactId>
+      <version>${elasticsearch.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>${elasticsearch.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <!-- log4j2 is default logging framework for ES server -->
+      <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>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>${maven-dependency-plugin.version}</version>
+        <executions>
+          <execution>
+            <id>analyze</id>
+            <goals>
+              <goal>analyze-only</goal>
+            </goals>
+            <configuration>
+              <failOnWarning>true</failOnWarning>
+              <!-- ignore "unused but declared" warnings -->
+              <ignoredUnusedDeclaredDependencies>
+                <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>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <!-- Parent module has the same plugin and does the work of
+          generating -sources.jar for each project. But without the
+          plugin declared here, IDEs don't know the sources are
+          available. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java
new file mode 100644
index 0000000..66aa1f7
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/AbstractElasticsearchTable.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.java.AbstractQueryableTable;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.linq4j.Queryable;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.impl.AbstractTableQueryable;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Table based on an Elasticsearch type.
+ */
+abstract class AbstractElasticsearchTable extends AbstractQueryableTable
+    implements TranslatableTable {
+
+  final String indexName;
+  final String typeName;
+
+  /**
+   * Creates an ElasticsearchTable.
+   * @param indexName Elastic Search index
+   * @param typeName Elastic Search index type
+   */
+  AbstractElasticsearchTable(String indexName, String typeName) {
+    super(Object[].class);
+    this.indexName = Objects.requireNonNull(indexName, "indexName");
+    this.typeName = Objects.requireNonNull(typeName, "typeName");
+  }
+
+  @Override public String toString() {
+    return "ElasticsearchTable{" + indexName + "/" + typeName + "}";
+  }
+
+  public RelDataType getRowType(RelDataTypeFactory relDataTypeFactory) {
+    final RelDataType mapType = relDataTypeFactory.createMapType(
+        relDataTypeFactory.createSqlType(SqlTypeName.VARCHAR),
+        relDataTypeFactory.createTypeWithNullability(
+            relDataTypeFactory.createSqlType(SqlTypeName.ANY),
+            true));
+    return relDataTypeFactory.builder().add("_MAP", mapType).build();
+  }
+
+  public <T> Queryable<T> asQueryable(QueryProvider queryProvider, SchemaPlus schema,
+      String tableName) {
+    return new ElasticsearchQueryable<>(queryProvider, schema, this, tableName);
+  }
+
+  public RelNode toRel(RelOptTable.ToRelContext context, RelOptTable relOptTable) {
+    final RelOptCluster cluster = context.getCluster();
+    return new ElasticsearchTableScan(cluster, cluster.traitSetOf(ElasticsearchRel.CONVENTION),
+        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.
+   * Used to keep backwards compatibility with ES2.
+   *
+   * @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>
+   * @return string to be used for scripted fields
+   */
+  protected abstract String scriptedFieldPrefix();
+
+  /** Executes a "find" operation on the underlying type.
+   *
+   * <p>For example,
+   * <code>client.prepareSearch(index).setTypes(type)
+   * .setSource("{\"fields\" : [\"state\"]}")</code></p>
+   *
+   * @param index Elasticsearch index
+   * @param ops List of operations represented as Json strings.
+   * @param fields List of fields to project; or null to return map
+   * @return Enumerator of results
+   */
+  protected abstract Enumerable<Object> find(String index, List<String> ops,
+      List<Map.Entry<String, Class>> fields);
+
+  /**
+   * Implementation of {@link Queryable} based on
+   * a {@link AbstractElasticsearchTable}.
+   *
+   * @param <T> element type
+   */
+  public static class ElasticsearchQueryable<T> extends AbstractTableQueryable<T> {
+    ElasticsearchQueryable(QueryProvider queryProvider, SchemaPlus schema,
+        AbstractElasticsearchTable table, String tableName) {
+      super(queryProvider, schema, table, tableName);
+    }
+
+    public Enumerator<T> enumerator() {
+      return null;
+    }
+
+    private String getIndex() {
+      return schema.unwrap(ElasticsearchSchema.class).getIndex();
+    }
+
+    private AbstractElasticsearchTable getTable() {
+      return (AbstractElasticsearchTable) table;
+    }
+
+    /** Called via code-generation.
+     * @param ops list of queries (as strings)
+     * @param fields projection
+     * @see ElasticsearchMethod#ELASTICSEARCH_QUERYABLE_FIND
+     * @return result as enumerable
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public Enumerable<Object> find(List<String> ops,
+        List<Map.Entry<String, Class>> fields) {
+      return getTable().find(getIndex(), ops, fields);
+    }
+  }
+}
+
+// End AbstractElasticsearchTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerators.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerators.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerators.java
new file mode 100644
index 0000000..d87de7e
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerators.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.tree.Primitive;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Util functions which convert
+ * {@link org.apache.calcite.adapter.elasticsearch.ElasticsearchSearchResult.SearchHit}
+ * into calcite specific return type (map, object[], list etc.)
+ */
+class ElasticsearchEnumerators {
+
+  private ElasticsearchEnumerators() {}
+
+  private static Function1<ElasticsearchSearchResult.SearchHit, Map> mapGetter() {
+    return new Function1<ElasticsearchSearchResult.SearchHit, Map>() {
+      public Map apply(ElasticsearchSearchResult.SearchHit hits) {
+        return hits.sourceOrFields();
+      }
+    };
+  }
+
+  private static Function1<ElasticsearchSearchResult.SearchHit, Object> singletonGetter(
+      final String fieldName,
+      final Class fieldClass) {
+    return new Function1<ElasticsearchSearchResult.SearchHit, Object>() {
+      public Object apply(ElasticsearchSearchResult.SearchHit hits) {
+        return convert(hits.sourceOrFields(), fieldClass);
+      }
+    };
+  }
+
+  /**
+   * Function that extracts a given set of fields from elastic search result
+   * objects.
+   *
+   * @param fields List of fields to project
+   *
+   * @return function that converts the search result into a generic array
+   */
+  private static Function1<ElasticsearchSearchResult.SearchHit, Object[]> listGetter(
+      final List<Map.Entry<String, Class>> fields) {
+    return new Function1<ElasticsearchSearchResult.SearchHit, Object[]>() {
+      public Object[] apply(ElasticsearchSearchResult.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();
+          final Class type = field.getValue();
+          objects[i] = convert(hit.value(name), type);
+        }
+        return objects;
+      }
+    };
+  }
+
+  static Function1<ElasticsearchSearchResult.SearchHit, Object> getter(
+      List<Map.Entry<String, Class>> fields) {
+    //noinspection unchecked
+    return fields == null
+      ? (Function1) mapGetter()
+      : fields.size() == 1
+      ? singletonGetter(fields.get(0).getKey(), fields.get(0).getValue())
+      : (Function1) listGetter(fields);
+  }
+
+  private static Object convert(Object o, Class clazz) {
+    if (o == null) {
+      return null;
+    }
+    Primitive primitive = Primitive.of(clazz);
+    if (primitive != null) {
+      clazz = primitive.boxClass;
+    } else {
+      primitive = Primitive.ofBox(clazz);
+    }
+    if (clazz.isInstance(o)) {
+      return o;
+    }
+    if (o instanceof Date && primitive != null) {
+      o = ((Date) o).getTime() / DateTimeUtils.MILLIS_PER_DAY;
+    }
+    if (o instanceof Number && primitive != null) {
+      return primitive.number((Number) o);
+    }
+    return o;
+  }
+}
+
+// End ElasticsearchEnumerators.java


[4/5] calcite git commit: [CALCITE-2376] Unify ES2 and ES5 adapters. Migrate to low-level ES rest client as main transport. (Andrei Sereda)

Posted by mm...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
new file mode 100644
index 0000000..c84d2c7
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.JsonBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of a {@link org.apache.calcite.rel.core.Filter}
+ * relational expression in Elasticsearch.
+ */
+public class ElasticsearchFilter extends Filter implements ElasticsearchRel {
+  ElasticsearchFilter(RelOptCluster cluster, RelTraitSet traitSet, RelNode child,
+      RexNode condition) {
+    super(cluster, traitSet, child, condition);
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+    assert getConvention() == child.getConvention();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
+  }
+
+  @Override public Filter copy(RelTraitSet relTraitSet, RelNode input, RexNode condition) {
+    return new ElasticsearchFilter(getCluster(), relTraitSet, input, condition);
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.visitChild(0, getInput());
+    List<String> fieldNames;
+    if (input instanceof Project) {
+      final List<RexNode> projects = ((Project) input).getProjects();
+      fieldNames = new ArrayList<>(projects.size());
+      for (RexNode project : projects) {
+        String name = project.accept(MapProjectionFieldVisitor.INSTANCE);
+        fieldNames.add(name);
+      }
+    } else {
+      fieldNames = ElasticsearchRules.elasticsearchFieldNames(getRowType());
+    }
+    Translator translator = new Translator(fieldNames);
+    String match = translator.translateMatch(condition);
+    implementor.add(match);
+  }
+
+  /**
+   * Translates {@link RexNode} expressions into Elasticsearch expression strings.
+   */
+  static class Translator {
+    final JsonBuilder builder = new JsonBuilder();
+    final Multimap<String, Pair<String, RexLiteral>> multimap =
+        HashMultimap.create();
+    final Map<String, RexLiteral> eqMap = new LinkedHashMap<>();
+    private final List<String> fieldNames;
+
+    Translator(List<String> fieldNames) {
+      this.fieldNames = fieldNames;
+    }
+
+    private String translateMatch(RexNode condition) {
+      // filter node
+      final Map<String, Object> filterMap = new LinkedHashMap<>();
+      filterMap.put("filter", translateOr(condition));
+
+      // constant_score node
+      final Map<String, Object> map = builder.map();
+      map.put("constant_score", filterMap);
+
+      return "\"query\" : " + builder.toJsonString(map).replaceAll("\\s+", "");
+    }
+
+    private Object translateOr(RexNode condition) {
+      final List<Object> list = new ArrayList<>();
+
+      final List<RexNode> orNodes = RelOptUtil.disjunctions(condition);
+      for (RexNode node : orNodes) {
+        List<Map<String, Object>> andNodes = translateAnd(node);
+
+        if (andNodes.size() > 0) {
+          Map<String, Object> andClause = new HashMap<>();
+          andClause.put("must", andNodes);
+
+          // boolean filters
+          LinkedHashMap<String, Object> filterEvaluator = new LinkedHashMap<>();
+          filterEvaluator.put("bool", andClause);
+          list.add(filterEvaluator);
+        } else {
+          list.add(andNodes.get(0));
+        }
+      }
+
+      if (orNodes.size() > 1) {
+        Map<String, Object> map = builder.map();
+        map.put("should", list);
+
+        // boolean filters
+        LinkedHashMap<String, Object> filterEvaluator = new LinkedHashMap<>();
+        filterEvaluator.put("bool", map);
+        return filterEvaluator;
+      } else {
+        return list.get(0);
+      }
+    }
+
+    private void addPredicate(Map<String, Object> map, String op, Object v) {
+      if (map.containsKey(op) && stronger(op, map.get(op), v)) {
+        return;
+      }
+      map.put(op, v);
+    }
+
+    /**
+     * Translates a condition that may be an AND of other conditions. Gathers
+     * together conditions that apply to the same field.
+     *
+     * @param node0 expression node
+     * @return list of elastic search term filters
+     */
+    private List<Map<String, Object>> translateAnd(RexNode node0) {
+      eqMap.clear();
+      multimap.clear();
+      for (RexNode node : RelOptUtil.conjunctions(node0)) {
+        translateMatch2(node);
+      }
+      List<Map<String, Object>> filters = new ArrayList<>();
+      for (Map.Entry<String, RexLiteral> entry : eqMap.entrySet()) {
+        multimap.removeAll(entry.getKey());
+
+        Map<String, Object> filter = new HashMap<>();
+        filter.put(entry.getKey(), literalValue(entry.getValue()));
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("term", filter);
+        filters.add(map);
+      }
+      for (Map.Entry<String, Collection<Pair<String, RexLiteral>>> entry
+          : multimap.asMap().entrySet()) {
+        Map<String, Object> map2 = builder.map();
+
+        Map<String, Object> map = new HashMap<>();
+        for (Pair<String, RexLiteral> s : entry.getValue()) {
+          if (!s.left.equals("not")) {
+            addPredicate(map2, s.left, literalValue(s.right));
+
+            Map<String, Object> filter = new HashMap<>();
+            filter.put(entry.getKey(), map2);
+
+            map.put("range", filter);
+          } else {
+            map2.put(entry.getKey(), literalValue(s.right));
+
+            Map<String, Object> termMap = new HashMap<>();
+            termMap.put("term", map2);
+
+            map.put("not", termMap);
+          }
+        }
+        filters.add(map);
+      }
+      return filters;
+    }
+
+    private boolean stronger(String key, Object v0, Object v1) {
+      if (key.equals("lt") || key.equals("lte")) {
+        if (v0 instanceof Number && v1 instanceof Number) {
+          return ((Number) v0).doubleValue() < ((Number) v1).doubleValue();
+        }
+        if (v0 instanceof String && v1 instanceof String) {
+          return v0.toString().compareTo(v1.toString()) < 0;
+        }
+      }
+      if (key.equals("gt") || key.equals("gte")) {
+        return stronger("lt", v1, v0);
+      }
+      return false;
+    }
+
+    private static Object literalValue(RexLiteral literal) {
+      return literal.getValue2();
+    }
+
+    private Void translateMatch2(RexNode node) {
+      switch (node.getKind()) {
+      case EQUALS:
+        return translateBinary(null, null, (RexCall) node);
+      case LESS_THAN:
+        return translateBinary("lt", "gt", (RexCall) node);
+      case LESS_THAN_OR_EQUAL:
+        return translateBinary("lte", "gte", (RexCall) node);
+      case NOT_EQUALS:
+        return translateBinary("not", "not", (RexCall) node);
+      case GREATER_THAN:
+        return translateBinary("gt", "lt", (RexCall) node);
+      case GREATER_THAN_OR_EQUAL:
+        return translateBinary("gte", "lte", (RexCall) node);
+      default:
+        throw new AssertionError("cannot translate " + node);
+      }
+    }
+
+    /**
+     * Translates a call to a binary operator, reversing arguments if
+     * necessary.
+     * @param op operation
+     * @param rop opposite operation of {@code op}
+     * @param call current relational call
+     * @return result can be ignored
+     */
+    private Void translateBinary(String op, String rop, RexCall call) {
+      final RexNode left = call.operands.get(0);
+      final RexNode right = call.operands.get(1);
+      boolean b = translateBinary2(op, left, right);
+      if (b) {
+        return null;
+      }
+      b = translateBinary2(rop, right, left);
+      if (b) {
+        return null;
+      }
+      throw new AssertionError("cannot translate op " + op + " call " + call);
+    }
+
+    /**
+     * Translates a call to a binary operator. Returns whether successful.
+     * @param op operation
+     * @param left left node of the expression
+     * @param right right node of the expression
+     * @return {@code true} if translation happened, {@code false} otherwise
+     */
+    private boolean translateBinary2(String op, RexNode left, RexNode right) {
+      switch (right.getKind()) {
+      case LITERAL:
+        break;
+      default:
+        return false;
+      }
+      final RexLiteral rightLiteral = (RexLiteral) right;
+      switch (left.getKind()) {
+      case INPUT_REF:
+        final RexInputRef left1 = (RexInputRef) left;
+        String name = fieldNames.get(left1.getIndex());
+        translateOp2(op, name, rightLiteral);
+        return true;
+      case CAST:
+        return translateBinary2(op, ((RexCall) left).operands.get(0), right);
+      case OTHER_FUNCTION:
+        String itemName = ElasticsearchRules.isItem((RexCall) left);
+        if (itemName != null) {
+          translateOp2(op, itemName, rightLiteral);
+          return true;
+        }
+        // fall through
+      default:
+        return false;
+      }
+    }
+
+    private void translateOp2(String op, String name, RexLiteral right) {
+      if (op == null) {
+        eqMap.put(name, right);
+      } else {
+        multimap.put(name, Pair.of(op, right));
+      }
+    }
+  }
+}
+
+// End ElasticsearchFilter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
new file mode 100644
index 0000000..72753e6
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.linq4j.tree.Types;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * Builtin methods in the Elasticsearch adapter.
+ */
+enum ElasticsearchMethod {
+  ELASTICSEARCH_QUERYABLE_FIND(AbstractElasticsearchTable.ElasticsearchQueryable.class,
+      "find", List.class, List.class);
+
+  public final Method method;
+
+  public static final ImmutableMap<Method, ElasticsearchMethod> MAP;
+
+  static {
+    final ImmutableMap.Builder<Method, ElasticsearchMethod> builder = ImmutableMap.builder();
+    for (ElasticsearchMethod value: ElasticsearchMethod.values()) {
+      builder.put(value.method, value);
+    }
+    MAP = builder.build();
+  }
+
+  ElasticsearchMethod(Class clazz, String methodName, Class... argumentTypes) {
+    this.method = Types.lookupMethod(clazz, methodName, argumentTypes);
+  }
+}
+
+// End ElasticsearchMethod.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java
new file mode 100644
index 0000000..e044703
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Project;
+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 com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Project}
+ * relational expression in Elasticsearch.
+ */
+public class ElasticsearchProject extends Project implements ElasticsearchRel {
+  ElasticsearchProject(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
+      List<? extends RexNode> projects, RelDataType rowType) {
+    super(cluster, traitSet, input, projects, rowType);
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+    assert getConvention() == input.getConvention();
+  }
+
+  @Override public Project copy(RelTraitSet relTraitSet, RelNode input, List<RexNode> projects,
+      RelDataType relDataType) {
+    return new ElasticsearchProject(getCluster(), traitSet, input, projects, relDataType);
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.visitChild(0, getInput());
+
+    final List<String> inFields =
+            ElasticsearchRules.elasticsearchFieldNames(getInput().getRowType());
+    final ElasticsearchRules.RexToElasticsearchTranslator translator =
+            new ElasticsearchRules.RexToElasticsearchTranslator(
+                    (JavaTypeFactory) getCluster().getTypeFactory(), inFields);
+
+    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 + "\"")) {
+        fields.add(name);
+      } else if (expr.matches("\"literal\":.+")) {
+        scriptFields.add(ElasticsearchRules.quote(name)
+                + ":{\"script\": "
+                + expr.split(":")[1] + "}");
+      } else {
+        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, ElasticsearchRules::quote);
+
+      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\": {")
+          .append(String.join(", ", scriptFields))
+          .append("}");
+    }
+
+    for (String opfield : implementor.list) {
+      if (opfield.startsWith("\"_source\"")) {
+        implementor.list.remove(opfield);
+      }
+    }
+    implementor.add(query.toString());
+  }
+}
+
+// End ElasticsearchProject.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
new file mode 100644
index 0000000..436adf9
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Relational expression that uses Elasticsearch calling convention.
+ */
+public interface ElasticsearchRel extends RelNode {
+  void implement(Implementor implementor);
+
+  /**
+   * Calling convention for relational operations that occur in Elasticsearch.
+   */
+  Convention CONVENTION = new Convention.Impl("ELASTICSEARCH", ElasticsearchRel.class);
+
+  /**
+   * Callback for the implementation process that converts a tree of
+   * {@link ElasticsearchRel} nodes into an Elasticsearch query.
+   */
+  class Implementor {
+    final List<String> list = new ArrayList<>();
+
+    RelOptTable table;
+    AbstractElasticsearchTable elasticsearchTable;
+
+    public void add(String findOp) {
+      list.add(findOp);
+    }
+
+    public void visitChild(int ordinal, RelNode input) {
+      assert ordinal == 0;
+      ((ElasticsearchRel) input).implement(this);
+    }
+  }
+}
+
+// End ElasticsearchRel.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
new file mode 100644
index 0000000..81b915b
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.enumerable.RexImpTable;
+import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelTrait;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Rules and relational operators for
+ * {@link ElasticsearchRel#CONVENTION ELASTICSEARCH}
+ * calling convention.
+ */
+class ElasticsearchRules {
+  static final RelOptRule[] RULES = {
+      ElasticsearchSortRule.INSTANCE,
+      ElasticsearchFilterRule.INSTANCE,
+      ElasticsearchProjectRule.INSTANCE
+  };
+
+  private ElasticsearchRules() {}
+
+  /**
+   * Returns 'string' if it is a call to item['string'], null otherwise.
+   * @param call current relational expression
+   * @return literal value
+   */
+  static String isItem(RexCall call) {
+    if (call.getOperator() != SqlStdOperatorTable.ITEM) {
+      return null;
+    }
+    final RexNode op0 = call.getOperands().get(0);
+    final RexNode op1 = call.getOperands().get(1);
+
+    if (op0 instanceof RexInputRef
+        && ((RexInputRef) op0).getIndex() == 0
+        && op1 instanceof RexLiteral
+        && ((RexLiteral) op1).getValue2() instanceof String) {
+      return (String) ((RexLiteral) op1).getValue2();
+    }
+    return null;
+  }
+
+  static List<String> elasticsearchFieldNames(final RelDataType rowType) {
+    return SqlValidatorUtil.uniquify(
+        new AbstractList<String>() {
+          @Override public String get(int index) {
+            final String name = rowType.getFieldList().get(index).getName();
+            return name.startsWith("$") ? "_" + name.substring(2) : name;
+          }
+
+          @Override public int size() {
+            return rowType.getFieldCount();
+          }
+        },
+        SqlValidatorUtil.EXPR_SUGGESTER, true);
+  }
+
+  static String quote(String s) {
+    return "\"" + s + "\"";
+  }
+
+  /**
+   * Translator from {@link RexNode} to strings in Elasticsearch's expression
+   * language.
+   */
+  static class RexToElasticsearchTranslator extends RexVisitorImpl<String> {
+    private final JavaTypeFactory typeFactory;
+    private final List<String> inFields;
+
+    RexToElasticsearchTranslator(JavaTypeFactory typeFactory, List<String> inFields) {
+      super(true);
+      this.typeFactory = typeFactory;
+      this.inFields = inFields;
+    }
+
+    @Override public String visitLiteral(RexLiteral literal) {
+      if (literal.getValue() == null) {
+        return "null";
+      }
+      return "\"literal\":\""
+        + RexToLixTranslator.translateLiteral(literal, literal.getType(),
+          typeFactory, RexImpTable.NullAs.NOT_POSSIBLE)
+        + "\"";
+    }
+
+    @Override public String visitInputRef(RexInputRef inputRef) {
+      return quote(inFields.get(inputRef.getIndex()));
+    }
+
+    @Override public String visitCall(RexCall call) {
+      final String name = isItem(call);
+      if (name != null) {
+        return "\"" + name + "\"";
+      }
+
+      final List<String> strings = visitList(call.operands);
+      if (call.getKind() == SqlKind.CAST) {
+        return strings.get(0).startsWith("$") ? strings.get(0).substring(1) : strings.get(0);
+      }
+      if (call.getOperator() == SqlStdOperatorTable.ITEM) {
+        final RexNode op1 = call.getOperands().get(1);
+        if (op1 instanceof RexLiteral && op1.getType().getSqlTypeName() == SqlTypeName.INTEGER) {
+          return stripQuotes(strings.get(0)) + "[" + ((RexLiteral) op1).getValue2() + "]";
+        }
+      }
+      throw new IllegalArgumentException("Translation of " + call.toString()
+        + "is not supported by ElasticsearchProject");
+    }
+
+    private String stripQuotes(String s) {
+      return s.startsWith("'") && s.endsWith("'") ? s.substring(1, s.length() - 1) : s;
+    }
+
+    List<String> visitList(List<RexNode> list) {
+      final List<String> strings = new ArrayList<>();
+      for (RexNode node: list) {
+        strings.add(node.accept(this));
+      }
+      return strings;
+    }
+  }
+
+  /**
+   * Base class for planner rules that convert a relational expression to
+   * Elasticsearch calling convention.
+   */
+  abstract static class ElasticsearchConverterRule extends ConverterRule {
+    final Convention out;
+
+    ElasticsearchConverterRule(Class<? extends RelNode> clazz, RelTrait in, Convention out,
+        String description) {
+      super(clazz, in, out, description);
+      this.out = out;
+    }
+  }
+
+  /**
+   * Rule to convert a {@link org.apache.calcite.rel.core.Sort} to an
+   * {@link ElasticsearchSort}.
+   */
+  private static class ElasticsearchSortRule extends ElasticsearchConverterRule {
+    private static final ElasticsearchSortRule INSTANCE =
+        new ElasticsearchSortRule();
+
+    private ElasticsearchSortRule() {
+      super(Sort.class, Convention.NONE, ElasticsearchRel.CONVENTION,
+          "ElasticsearchSortRule");
+    }
+
+    @Override public RelNode convert(RelNode relNode) {
+      final Sort sort = (Sort) relNode;
+      final RelTraitSet traitSet = sort.getTraitSet().replace(out).replace(sort.getCollation());
+      return new ElasticsearchSort(relNode.getCluster(), traitSet,
+        convert(sort.getInput(), traitSet.replace(RelCollations.EMPTY)), sort.getCollation(),
+        sort.offset, sort.fetch);
+    }
+  }
+
+  /**
+   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalFilter} to an
+   * {@link ElasticsearchFilter}.
+   */
+  private static class ElasticsearchFilterRule extends ElasticsearchConverterRule {
+    private static final ElasticsearchFilterRule INSTANCE = new ElasticsearchFilterRule();
+
+    private ElasticsearchFilterRule() {
+      super(LogicalFilter.class, Convention.NONE, ElasticsearchRel.CONVENTION,
+        "ElasticsearchFilterRule");
+    }
+
+    @Override public RelNode convert(RelNode relNode) {
+      final LogicalFilter filter = (LogicalFilter) relNode;
+      final RelTraitSet traitSet = filter.getTraitSet().replace(out);
+      return new ElasticsearchFilter(relNode.getCluster(), traitSet,
+        convert(filter.getInput(), out),
+        filter.getCondition());
+    }
+  }
+
+  /**
+   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalProject}
+   * to an {@link ElasticsearchProject}.
+   */
+  private static class ElasticsearchProjectRule extends ElasticsearchConverterRule {
+    private static final ElasticsearchProjectRule INSTANCE = new ElasticsearchProjectRule();
+
+    private ElasticsearchProjectRule() {
+      super(LogicalProject.class, Convention.NONE, ElasticsearchRel.CONVENTION,
+        "ElasticsearchProjectRule");
+    }
+
+    @Override public RelNode convert(RelNode relNode) {
+      final LogicalProject project = (LogicalProject) relNode;
+      final RelTraitSet traitSet = project.getTraitSet().replace(out);
+      return new ElasticsearchProject(project.getCluster(), traitSet,
+        convert(project.getInput(), out), project.getProjects(), project.getRowType());
+    }
+  }
+}
+
+// End ElasticsearchRules.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
new file mode 100644
index 0000000..a446615
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.impl.AbstractSchema;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.RestClient;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Schema mapped onto an index of ELASTICSEARCH types.
+ *
+ * <p>Each table in the schema is an ELASTICSEARCH type in that index.
+ */
+public class ElasticsearchSchema extends AbstractSchema {
+
+  private final String index;
+
+  private final RestClient client;
+
+  private final ObjectMapper mapper;
+
+  /**
+   * Allows schema to be instantiated from existing elastic search client.
+   * This constructor is used in tests.
+   * @param client existing client instance
+   * @param mapper mapper for JSON (de)serialization
+   * @param index name of ES index
+   */
+  ElasticsearchSchema(RestClient client, ObjectMapper mapper, String index) {
+    super();
+    this.client = Objects.requireNonNull(client, "client");
+    this.mapper = Objects.requireNonNull(mapper, "mapper");
+    this.index = Objects.requireNonNull(index, "index");
+  }
+
+  @Override protected Map<String, Table> getTableMap() {
+    final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
+    try {
+      for (String type: listTypes()) {
+        builder.put(type, new ElasticsearchTable(client, index, type));
+      }
+    } catch (IOException e) {
+      throw new UncheckedIOException("Failed to get types for " + index, e);
+    }
+    return builder.build();
+  }
+
+  /**
+   * Queries {@code _mapping} definition to automatically detect all types for an index
+   *
+   * @return list of types associated with this index
+   * @throws IOException for any IO related issues
+   * @throws IllegalStateException if reply is not understood
+   */
+  private Set<String> listTypes() throws IOException  {
+    final String endpoint = index + "/_mapping";
+    final Response response = client.performRequest("GET", endpoint);
+    try (InputStream is = response.getEntity().getContent()) {
+      JsonNode root = mapper.readTree(is);
+      if (!root.isObject() || root.size() != 1) {
+        final String message = String.format(Locale.ROOT, "Invalid response for %s/%s "
+            + "Expected object of size 1 got %s (of size %d)", response.getHost(),
+            response.getRequestLine(), root.getNodeType(), root.size());
+        throw new IllegalStateException(message);
+      }
+
+      JsonNode mappings = root.iterator().next().get("mappings");
+      if (mappings == null || mappings.size() == 0) {
+        final String message = String.format(Locale.ROOT, "Index %s does not have any types",
+            index);
+        throw new IllegalStateException(message);
+      }
+
+      Set<String> types = Sets.newHashSet(mappings.fieldNames());
+      types.remove("_default_");
+      return types;
+    }
+  }
+
+  public String getIndex() {
+    return index;
+  }
+}
+
+// End ElasticsearchSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java
new file mode 100644
index 0000000..5b93a51
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.SchemaFactory;
+import org.apache.calcite.schema.SchemaPlus;
+
+import org.apache.http.HttpHost;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.google.common.base.Preconditions;
+
+import org.elasticsearch.client.RestClient;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Factory that creates an {@link ElasticsearchSchema}.
+ *
+ * <p>Allows a custom schema to be included in a model.json file.
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class ElasticsearchSchemaFactory implements SchemaFactory {
+
+  public ElasticsearchSchemaFactory() {
+  }
+
+  @Override public Schema create(SchemaPlus parentSchema, String name,
+      Map<String, Object> operand) {
+
+    final Map map = (Map) operand;
+
+    final ObjectMapper mapper = new ObjectMapper();
+    mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+
+    try {
+      final Map<String, Integer> coordinates =
+          mapper.readValue((String) map.get("coordinates"),
+              new TypeReference<Map<String, Integer>>() { });
+
+      final RestClient client = connect(coordinates);
+
+      final Map<String, String> userConfig =
+          mapper.readValue((String) map.get("userConfig"),
+              new TypeReference<Map<String, String>>() { });
+
+      final String index = (String) map.get("index");
+      Preconditions.checkArgument(index != null, "index is missing in configuration");
+      return new ElasticsearchSchema(client, new ObjectMapper(), index);
+    } catch (IOException e) {
+      throw new RuntimeException("Cannot parse values from json", e);
+    }
+  }
+
+  /**
+   * Builds elastic rest client from user configuration
+   * @param coordinates list of {@code hostname/port} to connect to
+   * @return newly initialized low-level rest http client for ES
+   */
+  private static RestClient connect(Map<String, Integer> coordinates) {
+    Objects.requireNonNull(coordinates, "coordinates");
+    Preconditions.checkArgument(!coordinates.isEmpty(), "no ES coordinates specified");
+    final Set<HttpHost> set = new LinkedHashSet<>();
+    for (Map.Entry<String, Integer> entry: coordinates.entrySet()) {
+      set.add(new HttpHost(entry.getKey(), entry.getValue()));
+    }
+
+    return RestClient.builder(set.toArray(new HttpHost[0])).build();
+  }
+
+}
+
+// End ElasticsearchSchemaFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSearchResult.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSearchResult.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSearchResult.java
new file mode 100644
index 0000000..9bd19d8
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSearchResult.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.Duration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Internal object used to parse elastic search result. Similar to {@code SearchHit}.
+ * Since we're using row-level rest client the response has to be processed manually.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ElasticsearchSearchResult {
+
+  private final SearchHits hits;
+  private final long took;
+
+  /**
+   * Constructor for this instance.
+   * @param hits list of matched documents
+   * @param took time taken (in took) for this query to execute
+   */
+  @JsonCreator
+  ElasticsearchSearchResult(@JsonProperty("hits") SearchHits hits,
+                            @JsonProperty("took") long took) {
+    this.hits = Objects.requireNonNull(hits, "hits");
+    this.took = took;
+  }
+
+  public SearchHits searchHits() {
+    return hits;
+  }
+
+  public Duration took() {
+    return Duration.ofMillis(took);
+  }
+
+  /**
+   * Similar to {@code SearchHits} in ES. Container for {@link SearchHit}
+   */
+  @JsonIgnoreProperties(ignoreUnknown = true)
+  public static class SearchHits {
+
+    private final long total;
+    private final List<SearchHit> hits;
+
+    @JsonCreator
+    SearchHits(@JsonProperty("total")final long total,
+               @JsonProperty("hits") final List<SearchHit> hits) {
+      this.total = total;
+      this.hits = Objects.requireNonNull(hits, "hits");
+    }
+
+    public List<SearchHit> hits() {
+      return this.hits;
+    }
+
+    public long total() {
+      return total;
+    }
+
+  }
+
+  /**
+   * Concrete result record which matched the query. Similar to {@code SearchHit} in ES.
+   */
+  @JsonIgnoreProperties(ignoreUnknown = true)
+  public static class SearchHit {
+    private final String id;
+    private final Map<String, Object> source;
+    private final Map<String, Object> fields;
+
+    @JsonCreator
+    private SearchHit(@JsonProperty("_id") final String id,
+                      @JsonProperty("_source") final Map<String, Object> source,
+                      @JsonProperty("fields") final Map<String, Object> fields) {
+      this.id = Objects.requireNonNull(id, "id");
+
+      // both can't be null
+      if (source == null && fields == null) {
+        final String message = String.format(Locale.ROOT,
+            "Both '_source' and 'fields' are missing for %s", id);
+        throw new IllegalArgumentException(message);
+      }
+
+      // both can't be non-null
+      if (source != null && fields != null) {
+        final String message = String.format(Locale.ROOT,
+            "Both '_source' and 'fields' are populated (non-null) for %s", id);
+        throw new IllegalArgumentException(message);
+      }
+
+      this.source = source;
+      this.fields = fields;
+    }
+
+    /**
+     * Returns id of this hit (usually document id)
+     * @return unique id
+     */
+    public String id() {
+      return id;
+    }
+
+    /**
+     * Finds specific attribute from ES search result
+     * @param name attribute name
+     * @return value from result (_source or fields)
+     */
+    Object value(String name) {
+      Objects.requireNonNull(name, "name");
+
+      if (!sourceOrFields().containsKey(name)) {
+        final String message = String.format(Locale.ROOT,
+            "Attribute %s not found in search result %s", name, id);
+        throw new IllegalArgumentException(message);
+      }
+
+      if (source != null) {
+        return source.get(name);
+      } else if (fields != null) {
+        Object field = fields.get(name);
+        if (field instanceof Iterable) {
+          // return first element (or null)
+          Iterator<?> iter = ((Iterable<?>) field).iterator();
+          return iter.hasNext() ? iter.next() : null;
+        }
+
+        return field;
+      }
+
+      throw new AssertionError("Shouldn't get here: " + id);
+
+    }
+
+    public Map<String, Object> source() {
+      return source;
+    }
+
+    public Map<String, Object> fields() {
+      return fields;
+    }
+
+    public Map<String, Object> sourceOrFields() {
+      return source != null ? source : fields;
+    }
+  }
+
+}
+
+// End ElasticsearchSearchResult.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
new file mode 100644
index 0000000..ed669aa
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.Util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Sort}
+ * relational expression in Elasticsearch.
+ */
+public class ElasticsearchSort extends Sort implements ElasticsearchRel {
+  ElasticsearchSort(RelOptCluster cluster, RelTraitSet traitSet, RelNode child,
+      RelCollation collation, RexNode offset, RexNode fetch) {
+    super(cluster, traitSet, child, collation, offset, fetch);
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+    assert getConvention() == child.getConvention();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.05);
+  }
+
+  @Override public Sort copy(RelTraitSet traitSet, RelNode relNode, RelCollation relCollation,
+      RexNode offset, RexNode fetch) {
+    return new ElasticsearchSort(getCluster(), traitSet, relNode, collation, offset, fetch);
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.visitChild(0, getInput());
+    if (!collation.getFieldCollations().isEmpty()) {
+      final List<String> keys = new ArrayList<>();
+      if (input instanceof Project) {
+        final List<RexNode> projects = ((Project) input).getProjects();
+
+        for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
+          RexNode project = projects.get(fieldCollation.getFieldIndex());
+          String name = project.accept(MapProjectionFieldVisitor.INSTANCE);
+          keys.add(ElasticsearchRules.quote(name) + ": " + direction(fieldCollation));
+        }
+      } else {
+        final List<RelDataTypeField> fields = getRowType().getFieldList();
+
+        for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
+          final String name = fields.get(fieldCollation.getFieldIndex()).getName();
+          keys.add(ElasticsearchRules.quote(name) + ": " + direction(fieldCollation));
+        }
+      }
+
+      implementor.add("\"sort\": [ " + Util.toString(keys, "{", "}, {", "}") + "]");
+    }
+
+    if (offset != null) {
+      implementor.add("\"from\": " + ((RexLiteral) offset).getValue());
+    }
+
+    if (fetch != null) {
+      implementor.add("\"size\": " + ((RexLiteral) fetch).getValue());
+    }
+  }
+
+  private String direction(RelFieldCollation fieldCollation) {
+    switch (fieldCollation.getDirection()) {
+    case DESCENDING:
+    case STRICTLY_DESCENDING:
+      return "\"desc\"";
+    case ASCENDING:
+    case STRICTLY_ASCENDING:
+    default:
+      return "\"asc\"";
+    }
+  }
+}
+
+// End ElasticsearchSort.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java
new file mode 100644
index 0000000..7667563
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Linq4j;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.util.Util;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.util.EntityUtils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.RestClient;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Table based on an Elasticsearch type.
+ */
+public class ElasticsearchTable extends AbstractElasticsearchTable {
+  private final RestClient restClient;
+  private final ElasticsearchVersion version;
+  private final ObjectMapper mapper;
+
+  /**
+   * Creates an ElasticsearchTable.
+   * @param client low-level ES rest client
+   * @param indexName elastic search index
+   * @param typeName elastic searh index type
+   */
+  ElasticsearchTable(RestClient client, String indexName, String typeName) {
+    super(indexName, typeName);
+    this.restClient = Objects.requireNonNull(client, "client");
+    this.mapper = new ObjectMapper();
+    try {
+      this.version = detectVersion(client, mapper);
+    } catch (IOException e) {
+      final String message = String.format(Locale.ROOT, "Couldn't detect ES version "
+          + "for %s/%s", indexName, typeName);
+      throw new UncheckedIOException(message, e);
+    }
+
+  }
+
+  /**
+   * Detects current Elastic Search version by connecting to a existing instance.
+   * It is a {@code GET} request to {@code /}. Returned JSON has server information
+   * (including version).
+   *
+   * @param client low-level rest client connected to ES instance
+   * @param mapper Jackson mapper instance used to parse responses
+   * @return parsed version from ES, or {@link ElasticsearchVersion#UNKNOWN}
+   * @throws IOException if couldn't connect to ES
+   */
+  private static ElasticsearchVersion detectVersion(RestClient client, ObjectMapper mapper)
+      throws IOException {
+    HttpEntity entity = client.performRequest("GET", "/").getEntity();
+    JsonNode node = mapper.readTree(EntityUtils.toString(entity));
+    return ElasticsearchVersion.fromString(node.get("version").get("number").asText());
+  }
+
+  @Override protected String scriptedFieldPrefix() {
+    // ES2 vs ES5 scripted field difference
+    return version == ElasticsearchVersion.ES2 ? "_source" : "params._source";
+  }
+
+  @Override protected Enumerable<Object> find(String index, List<String> ops,
+      List<Map.Entry<String, Class>> fields) {
+
+    final String query;
+    if (!ops.isEmpty()) {
+      query = "{" + Util.toString(ops, "", ", ", "") + "}";
+    } else {
+      query = "{}";
+    }
+
+    try {
+      ElasticsearchSearchResult result = httpRequest(query);
+      final Function1<ElasticsearchSearchResult.SearchHit, Object> getter =
+          ElasticsearchEnumerators.getter(fields);
+      return Linq4j.asEnumerable(result.searchHits().hits()).select(getter);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  private ElasticsearchSearchResult httpRequest(String query) throws IOException {
+    Objects.requireNonNull(query, "query");
+    String uri = String.format(Locale.ROOT, "/%s/%s/_search", indexName, typeName);
+    HttpEntity entity = new StringEntity(query, ContentType.APPLICATION_JSON);
+    Response response = restClient.performRequest("POST", uri, Collections.emptyMap(), entity);
+
+    if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+      final String error = EntityUtils.toString(response.getEntity());
+      final String message = String.format(Locale.ROOT,
+          "Error while querying Elastic (on %s/%s) status: %s\nQuery:\n%s\nError:\n%s\n",
+          response.getHost(), response.getRequestLine(), response.getStatusLine(), query, error);
+      throw new RuntimeException(message);
+    }
+
+    try (InputStream is = response.getEntity().getContent()) {
+      return mapper.readValue(is, ElasticsearchSearchResult.class);
+    }
+  }
+}
+
+// End ElasticsearchTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
new file mode 100644
index 0000000..7795ad3
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Relational expression representing a scan of an Elasticsearch type.
+ *
+ * <p> Additional operations might be applied,
+ * using the "find" method.</p>
+ */
+public class ElasticsearchTableScan extends TableScan implements ElasticsearchRel {
+  private final AbstractElasticsearchTable elasticsearchTable;
+  private final RelDataType projectRowType;
+
+  /**
+   * Creates an ElasticsearchTableScan.
+   *
+   * @param cluster Cluster
+   * @param traitSet Trait set
+   * @param table Table
+   * @param elasticsearchTable Elasticsearch table
+   * @param projectRowType Fields and types to project; null to project raw row
+   */
+  ElasticsearchTableScan(RelOptCluster cluster, RelTraitSet traitSet,
+       RelOptTable table, AbstractElasticsearchTable elasticsearchTable,
+       RelDataType projectRowType) {
+    super(cluster, traitSet, table);
+    this.elasticsearchTable = Objects.requireNonNull(elasticsearchTable);
+    this.projectRowType = projectRowType;
+
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+  }
+
+  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+    assert inputs.isEmpty();
+    return this;
+  }
+
+  @Override public RelDataType deriveRowType() {
+    return projectRowType != null ? projectRowType : super.deriveRowType();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    final float f = projectRowType == null ? 1f : (float) projectRowType.getFieldCount() / 100f;
+    return super.computeSelfCost(planner, mq).multiplyBy(.1 * f);
+  }
+
+  @Override public void register(RelOptPlanner planner) {
+    planner.addRule(ElasticsearchToEnumerableConverterRule.INSTANCE);
+    for (RelOptRule rule: ElasticsearchRules.RULES) {
+      planner.addRule(rule);
+    }
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.elasticsearchTable = elasticsearchTable;
+    implementor.table = table;
+  }
+}
+
+// End ElasticsearchTableScan.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
new file mode 100644
index 0000000..d2896d3
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.enumerable.EnumerableRel;
+import org.apache.calcite.adapter.enumerable.EnumerableRelImplementor;
+import org.apache.calcite.adapter.enumerable.JavaRowFormat;
+import org.apache.calcite.adapter.enumerable.PhysType;
+import org.apache.calcite.adapter.enumerable.PhysTypeImpl;
+import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.linq4j.tree.MethodCallExpression;
+import org.apache.calcite.plan.ConventionTraitDef;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.prepare.CalcitePrepareImpl;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterImpl;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.runtime.Hook;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.Lists;
+
+import java.util.AbstractList;
+import java.util.List;
+
+/**
+ * Relational expression representing a scan of a table in an Elasticsearch data source.
+ */
+public class ElasticsearchToEnumerableConverter extends ConverterImpl implements EnumerableRel {
+  ElasticsearchToEnumerableConverter(RelOptCluster cluster, RelTraitSet traits,
+      RelNode input) {
+    super(cluster, ConventionTraitDef.INSTANCE, traits, input);
+  }
+
+  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+    return new ElasticsearchToEnumerableConverter(getCluster(), traitSet, sole(inputs));
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(.1);
+  }
+
+  @Override public Result implement(EnumerableRelImplementor implementor, Prefer prefer) {
+    final BlockBuilder list = new BlockBuilder();
+    final ElasticsearchRel.Implementor elasticsearchImplementor =
+        new ElasticsearchRel.Implementor();
+    elasticsearchImplementor.visitChild(0, getInput());
+    final RelDataType rowType = getRowType();
+    final PhysType physType = PhysTypeImpl.of(implementor.getTypeFactory(), rowType,
+        prefer.prefer(JavaRowFormat.ARRAY));
+    final Expression fields = list.append("fields",
+        constantArrayList(
+            Pair.zip(ElasticsearchRules.elasticsearchFieldNames(rowType),
+                new AbstractList<Class>() {
+                  @Override public Class get(int index) {
+                    return physType.fieldClass(index);
+                  }
+
+                  @Override public int size() {
+                    return rowType.getFieldCount();
+                  }
+                }),
+            Pair.class));
+    final Expression table = list.append("table",
+        elasticsearchImplementor.table
+            .getExpression(AbstractElasticsearchTable.ElasticsearchQueryable.class));
+    List<String> opList = elasticsearchImplementor.list;
+    final Expression ops = list.append("ops", constantArrayList(opList, String.class));
+    Expression enumerable = list.append("enumerable",
+        Expressions.call(table, ElasticsearchMethod.ELASTICSEARCH_QUERYABLE_FIND.method, ops,
+            fields));
+    if (CalcitePrepareImpl.DEBUG) {
+      System.out.println("Elasticsearch: " + opList);
+    }
+    Hook.QUERY_PLAN.run(opList);
+    list.add(Expressions.return_(null, enumerable));
+    return implementor.result(physType, list.toBlock());
+  }
+
+  /** E.g. {@code constantArrayList("x", "y")} returns
+   * "Arrays.asList('x', 'y')".
+   * @param values list of values
+   * @param clazz runtime class representing each element in the list
+   * @param <T> type of elements in the list
+   * @return method call which creates a list
+   */
+  private static <T> MethodCallExpression constantArrayList(List<T> values, Class clazz) {
+    return Expressions.call(BuiltInMethod.ARRAYS_AS_LIST.method,
+        Expressions.newArrayInit(clazz, constantList(values)));
+  }
+
+  /** E.g. {@code constantList("x", "y")} returns
+   * {@code {ConstantExpression("x"), ConstantExpression("y")}}.
+   * @param values list of elements
+   * @param <T> type of elements inside this list
+   * @return list of constant expressions
+   */
+  private static <T> List<Expression> constantList(List<T> values) {
+    return Lists.transform(values, Expressions::constant);
+  }
+}
+
+// End ElasticsearchToEnumerableConverter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
new file mode 100644
index 0000000..af7bbd6
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.enumerable.EnumerableConvention;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.tools.RelBuilderFactory;
+
+import java.util.function.Predicate;
+
+/**
+ * Rule to convert a relational expression from
+ * {@link ElasticsearchRel#CONVENTION} to {@link EnumerableConvention}.
+ */
+public class ElasticsearchToEnumerableConverterRule extends ConverterRule {
+  static final ConverterRule INSTANCE =
+      new ElasticsearchToEnumerableConverterRule(RelFactories.LOGICAL_BUILDER);
+
+  /**
+   * Creates an ElasticsearchToEnumerableConverterRule.
+   *
+   * @param relBuilderFactory Builder for relational expressions
+   */
+  ElasticsearchToEnumerableConverterRule(
+      RelBuilderFactory relBuilderFactory) {
+    super(RelNode.class, (Predicate<RelNode>) r -> true,
+        ElasticsearchRel.CONVENTION, EnumerableConvention.INSTANCE,
+        relBuilderFactory, "ElasticsearchToEnumerableConverterRule");
+  }
+
+  @Override public RelNode convert(RelNode relNode) {
+    RelTraitSet newTraitSet = relNode.getTraitSet().replace(getOutConvention());
+    return new ElasticsearchToEnumerableConverter(relNode.getCluster(), newTraitSet, relNode);
+  }
+}
+
+// End ElasticsearchToEnumerableConverterRule.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersion.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersion.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersion.java
new file mode 100644
index 0000000..3d774dd
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchVersion.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Identifies current ES version at runtime. Some queries have different syntax
+ * depending on version (eg. 2 vs 5).
+ */
+enum ElasticsearchVersion {
+
+  ES2,
+  ES5,
+  ES6,
+  ES7,
+  UNKNOWN;
+
+  static ElasticsearchVersion fromString(String version) {
+    Objects.requireNonNull(version, "version");
+    if (!version.matches("\\d+\\.\\d+\\.\\d+")) {
+      final String message = String.format(Locale.ROOT, "Wrong version format. "
+          + "Expected ${digit}.${digit}.${digit} but got %s", version);
+      throw new IllegalArgumentException(message);
+    }
+
+    // version format is: major.minor.revision
+    final int major = Integer.parseInt(version.substring(0, version.indexOf(".")));
+    if (major == 2) {
+      return ES2;
+    } else if (major == 5) {
+      return ES5;
+    } else if (major == 6) {
+      return ES6;
+    } else if (major == 7) {
+      return ES7;
+    } else {
+      return UNKNOWN;
+    }
+  }
+}
+
+// End ElasticsearchVersion.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java
new file mode 100644
index 0000000..0e5b556
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/MapProjectionFieldVisitor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+
+/**
+ * Visitor that extracts the actual field name from an item expression.
+ */
+class MapProjectionFieldVisitor extends RexVisitorImpl<String> {
+
+  static final MapProjectionFieldVisitor INSTANCE = new MapProjectionFieldVisitor();
+
+  private MapProjectionFieldVisitor() {
+    super(true);
+  }
+
+  @Override public String visitCall(RexCall call) {
+    if (call.op == SqlStdOperatorTable.ITEM) {
+      return ((RexLiteral) call.getOperands().get(1)).getValueAs(String.class);
+    }
+    return super.visitCall(call);
+  }
+}
+
+// End MapProjectionFieldVisitor.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/98838180/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
new file mode 100644
index 0000000..ee49d60
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Query provider based on an Elasticsearch2 DB.
+ */
+@PackageMarker
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java