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 2017/09/05 14:36:46 UTC

[03/16] calcite git commit: [CALCITE-1967] Elasticsearch 5 adapter (Christian Beikov)

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/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
new file mode 100644
index 0000000..636aa5f
--- /dev/null
+++ b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/Elasticsearch2Table.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+   */
+  public Elasticsearch2Table(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 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/e1525926/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
new file mode 100644
index 0000000..a1c10c9
--- /dev/null
+++ b/elasticsearch2/src/main/java/org/apache/calcite/adapter/elasticsearch2/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.elasticsearch2;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/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
new file mode 100644
index 0000000..4e0c2b6
--- /dev/null
+++ b/elasticsearch2/src/test/java/org/apache/calcite/test/Elasticsearch2AdapterIT.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Test;
+
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Tests for the {@code org.apache.calcite.adapter.elasticsearch2} package.
+ *
+ * <p>Before calling this test, you need to populate Elasticsearch, as follows:
+ *
+ * <blockquote><code>
+ * git clone https://github.com/vlsi/calcite-test-dataset<br>
+ * cd calcite-test-dataset<br>
+ * mvn install
+ * </code></blockquote>
+ *
+ * <p>This will create a virtual machine with Elasticsearch and the "zips" test
+ * dataset.
+ */
+public class Elasticsearch2AdapterIT {
+  /**
+   * Whether to run Elasticsearch tests. Enabled by default, however test is only
+   * included if "it" profile is activated ({@code -Pit}). To disable,
+   * specify {@code -Dcalcite.test.elasticsearch=false} on the Java command line.
+   */
+  private static final boolean ENABLED = Util.getBooleanProperty("calcite.test.elasticsearch",
+      true);
+
+  /** Connection factory based on the "zips-es" model. */
+  private static final ImmutableMap<String, String> ZIPS = ImmutableMap.of("model",
+      Elasticsearch2AdapterIT.class.getResource("/elasticsearch-zips-model.json").getPath());
+
+  /** Whether to run this test. */
+  private boolean enabled() {
+    return ENABLED;
+  }
+
+  /** Returns a function that checks that a particular Elasticsearch pipeline is
+   * generated to implement a query. */
+  private static Function<List, Void> elasticsearchChecker(final String... strings) {
+    return new Function<List, Void>() {
+      @Nullable
+      @Override public Void apply(@Nullable List actual) {
+        Object[] actualArray = actual == null || actual.isEmpty() ? null
+            : ((List) actual.get(0)).toArray();
+        CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
+            actualArray);
+        return null;
+      }
+    };
+  }
+
+  @Test public void testSort() {
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchSort(sort0=[$4], dir0=[ASC])\n"
+        + "    ElasticsearchProject(city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "      ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select * from zips order by \"state\"")
+        .returnsCount(10)
+        .explainContains(explain);
+  }
+
+  @Test public void testSortLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "order by \"state\", \"id\" offset 2 rows fetch next 3 rows only";
+    CalciteAssert.that()
+        .with(ZIPS)
+        .query(sql)
+        .returnsUnordered("state=AK; id=99503",
+            "state=AK; id=99504",
+            "state=AK; id=99505")
+        .queryContains(
+            elasticsearchChecker(
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"id\": \"asc\"}]",
+                "\"from\": 2",
+                "\"size\": 3"));
+  }
+
+  @Test public void testOffsetLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "offset 2 fetch next 3 rows only";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(
+                "\"from\": 2",
+                "\"size\": 3",
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
+  }
+
+  @Test public void testLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "fetch next 3 rows only";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(
+                "\"size\": 3",
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
+  }
+
+  @Test public void testFilterSort() {
+    final String sql = "select * from zips\n"
+        + "where \"city\" = 'SPRINGFIELD' and \"id\" >= '70000'\n"
+        + "order by \"state\", \"id\"";
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchSort(sort0=[$4], sort1=[$5], dir0=[ASC], dir1=[ASC])\n"
+        + "    ElasticsearchProject(city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "      ElasticsearchFilter(condition=[AND(=(CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", 'SPRINGFIELD'), >=(CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", '70000'))])\n"
+        + "        ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .returnsOrdered(
+            "city=SPRINGFIELD; longitude=-92.54567; latitude=35.274879; pop=752; state=AR; id=72157",
+            "city=SPRINGFIELD; longitude=-102.617322; latitude=37.406727; pop=1992; state=CO; id=81073",
+            "city=SPRINGFIELD; longitude=-90.577479; latitude=30.415738; pop=5597; state=LA; id=70462",
+            "city=SPRINGFIELD; longitude=-123.015259; latitude=44.06106; pop=32384; state=OR; id=97477",
+            "city=SPRINGFIELD; longitude=-122.917108; latitude=44.056056; pop=27521; state=OR; id=97478")
+        .queryContains(
+            elasticsearchChecker("\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
+                    + "{\"must\":[{\"term\":{\"city\":\"springfield\"}},{\"range\":{\"id\":{\"gte\":\"70000\"}}}]}}}}",
+                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"id\": \"asc\"}]"))
+        .explainContains(explain);
+  }
+
+  @Test public void testFilterSortDesc() {
+    final String sql = "select * from zips\n"
+        + "where \"pop\" BETWEEN 20000 AND 20100\n"
+        + "order by \"state\" desc, \"pop\"";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .limit(4)
+        .returnsOrdered(
+            "city=SHERIDAN; longitude=-106.964795; latitude=44.78486; pop=20025; state=WY; id=82801",
+            "city=MOUNTLAKE TERRAC; longitude=-122.304036; latitude=47.793061; pop=20059; state=WA; id=98043",
+            "city=FALMOUTH; longitude=-77.404537; latitude=38.314557; pop=20039; state=VA; id=22405",
+            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104");
+  }
+
+  @Test public void testFilterRedundant() {
+    final String sql = "select * from zips\n"
+        + "where \"state\" > 'CA' and \"state\" < 'AZ' and \"state\" = 'OK'";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(""
+                + "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
+                + "{\"must\":[{\"term\":{\"state\":\"ok\"}}]}}}}",
+                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"));
+  }
+
+  @Test public void testInPlan() {
+    final String[] searches = {
+        "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":{\"should\":"
+          + "[{\"bool\":{\"must\":[{\"term\":{\"pop\":20012}}]}},{\"bool\":{\"must\":[{\"term\":"
+          + "{\"pop\":15590}}]}}]}}}}",
+        "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"
+    };
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select * from zips where \"pop\" in (20012, 15590)")
+        .returnsUnordered(
+            "city=COVINA; longitude=-117.884285; latitude=34.08596; pop=15590; state=CA; id=91723",
+            "city=ARLINGTON; longitude=-97.091987; latitude=32.654752; pop=15590; state=TX; id=76018",
+            "city=CROFTON; longitude=-76.680166; latitude=39.011163; pop=15590; state=MD; id=21114",
+            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104",
+            "city=DINUBA; longitude=-119.39087; latitude=36.534931; pop=20012; state=CA; id=93618")
+        .queryContains(elasticsearchChecker(searches));
+  }
+
+  @Test public void testZips() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips")
+        .returnsCount(10);
+  }
+
+  @Test public void testProject() {
+    final String sql = "select \"state\", \"city\", 0 as \"zero\"\n"
+        + "from zips\n"
+        + "order by \"state\", \"city\"";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .limit(2)
+        .returnsUnordered("state=AK; city=ELMENDORF AFB; zero=0",
+            "state=AK; city=EIELSON AFB; zero=0")
+        .queryContains(
+            elasticsearchChecker("\"sort\": [ {\"state\": \"asc\"}, {\"city\": \"asc\"}]",
+                "\"fields\" : [\"state\", \"city\"], \"script_fields\": {\"zero\":{\"script\": \"0\"}}"));
+  }
+
+  @Test public void testFilter() {
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchProject(state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "    ElasticsearchFilter(condition=[=(CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", 'CA')])\n"
+        + "      ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where \"state\" = 'CA'")
+        .limit(2)
+        .returnsUnordered("state=CA; city=LOS ANGELES",
+            "state=CA; city=LOS ANGELES")
+        .explainContains(explain);
+  }
+
+  @Test public void testFilterReversed() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where 'WI' < \"state\"")
+        .limit(2)
+        .returnsUnordered("state=WV; city=WELCH",
+            "state=WV; city=HANOVER");
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where \"state\" > 'WI'")
+        .limit(2)
+        .returnsUnordered("state=WV; city=WELCH",
+            "state=WV; city=HANOVER");
+  }
+}
+
+// End Elasticsearch2AdapterIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/elasticsearch2/src/test/resources/elasticsearch-zips-model.json
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/test/resources/elasticsearch-zips-model.json b/elasticsearch2/src/test/resources/elasticsearch-zips-model.json
new file mode 100644
index 0000000..6c00a9c
--- /dev/null
+++ b/elasticsearch2/src/test/resources/elasticsearch-zips-model.json
@@ -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.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "elasticsearch",
+  "schemas": [
+    {
+      "type": "custom",
+      "name": "elasticsearch_raw",
+      "factory": "org.apache.calcite.adapter.elasticsearch2.Elasticsearch2SchemaFactory",
+      "operand": {
+        "coordinates": "{'127.0.0.1': 9300}",
+        "userConfig": "{'bulk.flush.max.actions': 10, 'bulk.flush.max.size.mb': 1}",
+        "index": "usa"
+      }
+    },
+    {
+      "name": "elasticsearch",
+      "tables": [
+        {
+          "name": "ZIPS",
+          "type": "view",
+          "sql": [
+            "select cast(_MAP['city'] AS varchar(20)) AS \"city\",\n",
+            " cast(_MAP['loc'][0] AS float) AS \"longitude\",\n",
+            " cast(_MAP['loc'][1] AS float) AS \"latitude\",\n",
+            " cast(_MAP['pop'] AS integer) AS \"pop\",\n",
+            " cast(_MAP['state'] AS varchar(2)) AS \"state\",\n",
+            " cast(_MAP['id'] AS varchar(5)) AS \"id\"\n",
+            "from \"elasticsearch_raw\".\"zips\""
+          ]
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/elasticsearch2/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/elasticsearch2/src/test/resources/log4j.properties b/elasticsearch2/src/test/resources/log4j.properties
new file mode 100644
index 0000000..834e2db
--- /dev/null
+++ b/elasticsearch2/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+# 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/e1525926/elasticsearch5/pom.xml
----------------------------------------------------------------------
diff --git a/elasticsearch5/pom.xml b/elasticsearch5/pom.xml
new file mode 100644
index 0000000..a599bac
--- /dev/null
+++ b/elasticsearch5/pom.xml
@@ -0,0 +1,153 @@
+<?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.14.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-elasticsearch5</artifactId>
+  <packaging>jar</packaging>
+  <version>1.14.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>com.carrotsearch</groupId>
+      <artifactId>hppc</artifactId>
+      <version>${hppc.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.code.findbugs</groupId>
+      <artifactId>jsr305</artifactId>
+    </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/e1525926/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
new file mode 100644
index 0000000..262058a
--- /dev/null
+++ b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Enumerator.java
@@ -0,0 +1,154 @@
+/*
+ * 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 new Function1<SearchHit, Map>() {
+      public Map apply(SearchHit searchHitFields) {
+        return (Map) searchHitFields.getFields();
+      }
+    };
+  }
+
+  private static Function1<SearchHit, Object> singletonGetter(final String fieldName,
+      final Class fieldClass) {
+    return new Function1<SearchHit, Object>() {
+      public Object apply(SearchHit 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
+   */
+  private static Function1<SearchHit, Object[]> listGetter(
+      final List<Map.Entry<String, Class>> fields) {
+    return new Function1<SearchHit, Object[]>() {
+      public Object[] apply(SearchHit 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/e1525926/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
new file mode 100644
index 0000000..6e16f2f
--- /dev/null
+++ b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Schema.java
@@ -0,0 +1,138 @@
+/*
+ * 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.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;
+
+/**
+ * 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;
+    }
+  }
+
+  @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 RuntimeException("Cannot connect to any elasticsearch nodes");
+    }
+
+    client = transportClient;
+  }
+
+  @Override public String getIndex() {
+    return index;
+  }
+}
+
+// End Elasticsearch5Schema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/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
new file mode 100644
index 0000000..b573d37
--- /dev/null
+++ b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5SchemaFactory.java
@@ -0,0 +1,63 @@
+/*
+ * 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/e1525926/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
new file mode 100644
index 0000000..175e3cc
--- /dev/null
+++ b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/Elasticsearch5Table.java
@@ -0,0 +1,86 @@
+/*
+ * 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.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.builder.SearchSourceBuilder;
+
+import java.io.IOException;
+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.
+   */
+  public 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, "", ", ", "") + "}";
+      NamedXContentRegistry xContentRegistry = NamedXContentRegistry.EMPTY;
+      XContent xContent = JsonXContent.jsonXContent;
+      try (XContentParser parser = xContent.createParser(xContentRegistry, queryString)) {
+        final QueryParseContext queryParseContext = new QueryParseContext(parser);
+        searchSourceBuilder = SearchSourceBuilder.fromXContent(queryParseContext);
+      } catch (IOException ex) {
+        throw new RuntimeException(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/e1525926/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
new file mode 100644
index 0000000..70c4695
--- /dev/null
+++ b/elasticsearch5/src/main/java/org/apache/calcite/adapter/elasticsearch5/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 Elasticsearch5 DB.
+ */
+@PackageMarker
+package org.apache.calcite.adapter.elasticsearch5;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/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
new file mode 100644
index 0000000..1cf52fe
--- /dev/null
+++ b/elasticsearch5/src/test/java/org/apache/calcite/test/Elasticsearch5AdapterIT.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Test;
+
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Tests for the {@code org.apache.calcite.adapter.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 Function<List, Void> elasticsearchChecker(final String... strings) {
+    return new Function<List, Void>() {
+      @Nullable
+      @Override public Void apply(@Nullable List actual) {
+        Object[] actualArray = actual == null || actual.isEmpty() ? null
+            : ((List) actual.get(0)).toArray();
+        CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
+            actualArray);
+        return null;
+      }
+    };
+  }
+
+  @Test public void testSort() {
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchSort(sort0=[$4], dir0=[ASC])\n"
+        + "    ElasticsearchProject(city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "      ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select * from zips order by \"state\"")
+        .returnsCount(10)
+        .explainContains(explain);
+  }
+
+  @Test public void testSortLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "order by \"state\", \"id\" offset 2 rows fetch next 3 rows only";
+    CalciteAssert.that()
+        .with(ZIPS)
+        .query(sql)
+        .returnsUnordered("state=AK; id=99503",
+            "state=AK; id=99504",
+            "state=AK; id=99505")
+        .queryContains(
+            elasticsearchChecker(
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"id\": \"asc\"}]",
+                "\"from\": 2",
+                "\"size\": 3"));
+  }
+
+  @Test public void testOffsetLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "offset 2 fetch next 3 rows only";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(
+                "\"from\": 2",
+                "\"size\": 3",
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
+  }
+
+  @Test public void testLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "fetch next 3 rows only";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(
+                "\"size\": 3",
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
+  }
+
+  @Test public void testFilterSort() {
+    final String sql = "select * from zips\n"
+        + "where \"city\" = 'SPRINGFIELD' and \"id\" >= '70000'\n"
+        + "order by \"state\", \"id\"";
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchSort(sort0=[$4], sort1=[$5], dir0=[ASC], dir1=[ASC])\n"
+        + "    ElasticsearchProject(city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "      ElasticsearchFilter(condition=[AND(=(CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", 'SPRINGFIELD'), >=(CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", '70000'))])\n"
+        + "        ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .returnsOrdered(
+            "city=SPRINGFIELD; longitude=-92.54567; latitude=35.274879; pop=752; state=AR; id=72157",
+            "city=SPRINGFIELD; longitude=-102.617322; latitude=37.406727; pop=1992; state=CO; id=81073",
+            "city=SPRINGFIELD; longitude=-90.577479; latitude=30.415738; pop=5597; state=LA; id=70462",
+            "city=SPRINGFIELD; longitude=-123.015259; latitude=44.06106; pop=32384; state=OR; id=97477",
+            "city=SPRINGFIELD; longitude=-122.917108; latitude=44.056056; pop=27521; state=OR; id=97478")
+        .queryContains(
+            elasticsearchChecker("\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
+                    + "{\"must\":[{\"term\":{\"city\":\"springfield\"}},{\"range\":{\"id\":{\"gte\":\"70000\"}}}]}}}}",
+                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"id\": \"asc\"}]"))
+        .explainContains(explain);
+  }
+
+  @Test public void testFilterSortDesc() {
+    final String sql = "select * from zips\n"
+        + "where \"pop\" BETWEEN 20000 AND 20100\n"
+        + "order by \"state\" desc, \"pop\"";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .limit(4)
+        .returnsOrdered(
+            "city=SHERIDAN; longitude=-106.964795; latitude=44.78486; pop=20025; state=WY; id=82801",
+            "city=MOUNTLAKE TERRAC; longitude=-122.304036; latitude=47.793061; pop=20059; state=WA; id=98043",
+            "city=FALMOUTH; longitude=-77.404537; latitude=38.314557; pop=20039; state=VA; id=22405",
+            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104");
+  }
+
+  @Test public void testFilterRedundant() {
+    final String sql = "select * from zips\n"
+        + "where \"state\" > 'CA' and \"state\" < 'AZ' and \"state\" = 'OK'";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(""
+                + "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
+                + "{\"must\":[{\"term\":{\"state\":\"ok\"}}]}}}}",
+                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"));
+  }
+
+  @Test public void testInPlan() {
+    final String[] searches = {
+        "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":{\"should\":"
+          + "[{\"bool\":{\"must\":[{\"term\":{\"pop\":20012}}]}},{\"bool\":{\"must\":[{\"term\":"
+          + "{\"pop\":15590}}]}}]}}}}",
+        "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"
+    };
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select * from zips where \"pop\" in (20012, 15590)")
+        .returnsUnordered(
+            "city=COVINA; longitude=-117.884285; latitude=34.08596; pop=15590; state=CA; id=91723",
+            "city=ARLINGTON; longitude=-97.091987; latitude=32.654752; pop=15590; state=TX; id=76018",
+            "city=CROFTON; longitude=-76.680166; latitude=39.011163; pop=15590; state=MD; id=21114",
+            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104",
+            "city=DINUBA; longitude=-119.39087; latitude=36.534931; pop=20012; state=CA; id=93618")
+        .queryContains(elasticsearchChecker(searches));
+  }
+
+  @Test public void testZips() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips")
+        .returnsCount(10);
+  }
+
+  @Test public void testProject() {
+    final String sql = "select \"state\", \"city\", 0 as \"zero\"\n"
+        + "from zips\n"
+        + "order by \"state\", \"city\"";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .limit(2)
+        .returnsUnordered("state=AK; city=ELMENDORF AFB; zero=0",
+            "state=AK; city=EIELSON AFB; zero=0")
+        .queryContains(
+            elasticsearchChecker("\"sort\": [ {\"state\": \"asc\"}, {\"city\": \"asc\"}]",
+                "\"fields\" : [\"state\", \"city\"], \"script_fields\": {\"zero\":{\"script\": \"0\"}}"));
+  }
+
+  @Test public void testFilter() {
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchProject(state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "    ElasticsearchFilter(condition=[=(CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", 'CA')])\n"
+        + "      ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where \"state\" = 'CA'")
+        .limit(2)
+        .returnsUnordered("state=CA; city=LOS ANGELES",
+            "state=CA; city=LOS ANGELES")
+        .explainContains(explain);
+  }
+
+  @Test public void testFilterReversed() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where 'WI' < \"state\"")
+        .limit(2)
+        .returnsUnordered("state=WV; city=WELCH",
+            "state=WV; city=HANOVER");
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where \"state\" > 'WI'")
+        .limit(2)
+        .returnsUnordered("state=WV; city=WELCH",
+            "state=WV; city=HANOVER");
+  }
+}
+
+// End Elasticsearch5AdapterIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/elasticsearch5/src/test/resources/elasticsearch-zips-model.json
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/resources/elasticsearch-zips-model.json b/elasticsearch5/src/test/resources/elasticsearch-zips-model.json
new file mode 100644
index 0000000..626923e
--- /dev/null
+++ b/elasticsearch5/src/test/resources/elasticsearch-zips-model.json
@@ -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.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "elasticsearch",
+  "schemas": [
+    {
+      "type": "custom",
+      "name": "elasticsearch_raw",
+      "factory": "org.apache.calcite.adapter.elasticsearch5.Elasticsearch5SchemaFactory",
+      "operand": {
+        "coordinates": "{'127.0.0.1': 9300}",
+        "userConfig": "{'bulk.flush.max.actions': 10, 'bulk.flush.max.size.mb': 1}",
+        "index": "usa"
+      }
+    },
+    {
+      "name": "elasticsearch",
+      "tables": [
+        {
+          "name": "ZIPS",
+          "type": "view",
+          "sql": [
+            "select cast(_MAP['city'] AS varchar(20)) AS \"city\",\n",
+            " cast(_MAP['loc'][0] AS float) AS \"longitude\",\n",
+            " cast(_MAP['loc'][1] AS float) AS \"latitude\",\n",
+            " cast(_MAP['pop'] AS integer) AS \"pop\",\n",
+            " cast(_MAP['state'] AS varchar(2)) AS \"state\",\n",
+            " cast(_MAP['id'] AS varchar(5)) AS \"id\"\n",
+            "from \"elasticsearch_raw\".\"zips\""
+          ]
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/elasticsearch5/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/elasticsearch5/src/test/resources/log4j.properties b/elasticsearch5/src/test/resources/log4j.properties
new file mode 100644
index 0000000..834e2db
--- /dev/null
+++ b/elasticsearch5/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+# 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/e1525926/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 3372f99..a8f4df3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@ limitations under the License.
     <commons-lang3.version>3.2</commons-lang3.version>
     <commons-logging.version>1.1.3</commons-logging.version>
     <elasticsearch-java-driver.version>2.3.2</elasticsearch-java-driver.version>
+    <elasticsearch5-java-driver.version>5.5.2</elasticsearch5-java-driver.version>
     <findbugs.version>3.0.1</findbugs.version>
     <fmpp-maven-plugin.version>1.0</fmpp-maven-plugin.version>
     <foodmart-data-hsqldb.version>0.3</foodmart-data-hsqldb.version>
@@ -139,7 +140,8 @@ limitations under the License.
     <module>cassandra</module>
     <module>core</module>
     <module>druid</module>
-    <module>elasticsearch</module>
+    <module>elasticsearch2</module>
+    <module>elasticsearch5</module>
     <module>example</module>
     <module>file</module>
     <module>linq4j</module>

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/site/_docs/adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/adapter.md b/site/_docs/adapter.md
index ef8e761..3987804 100644
--- a/site/_docs/adapter.md
+++ b/site/_docs/adapter.md
@@ -30,7 +30,10 @@ presenting the data as tables within a schema.
 * [Cassandra adapter](cassandra_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/cassandra/package-summary.html">calcite-cassandra</a>)
 * CSV adapter (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/csv/package-summary.html">example/csv</a>)
 * [Druid adapter](druid_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/druid/package-summary.html">calcite-druid</a>)
-* [Elasticsearch adapter](elasticsearch_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/elasticsearch/package-summary.html">calcite-elasticsearch</a>)
+* [Elasticsearch adapter](elasticsearch_adapter.html)
+  (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/elasticsearch2/package-summary.html">calcite-elasticsearch2</a>
+  and
+  <a href="{{ site.apiRoot }}/org/apache/calcite/adapter/elasticsearch5/package-summary.html">calcite-elasticsearch5</a>)
 * [File adapter](file_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/file/package-summary.html">calcite-file</a>)
 * JDBC adapter (part of <a href="{{ site.apiRoot }}/org/apache/calcite/adapter/jdbc/package-summary.html">calcite-core</a>)
 * MongoDB adapter (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/mongodb/package-summary.html">calcite-mongodb</a>)

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/site/_docs/elasticsearch_adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/elasticsearch_adapter.md b/site/_docs/elasticsearch_adapter.md
index d87d9e3..21f2459 100644
--- a/site/_docs/elasticsearch_adapter.md
+++ b/site/_docs/elasticsearch_adapter.md
@@ -47,7 +47,7 @@ A basic example of a model file is given below:
     {
       "type": "custom",
       "name": "elasticsearch",
-      "factory": "org.apache.calcite.adapter.elasticsearch.ElasticsearchSchemaFactory",
+      "factory": "org.apache.calcite.adapter.elasticsearch2.Elasticsearch2SchemaFactory",
       "operand": {
         "coordinates": "{'127.0.0.1': 9300}",
         "userConfig": "{'bulk.flush.max.actions': 10, 'bulk.flush.max.size.mb': 1}",
@@ -58,6 +58,9 @@ A basic example of a model file is given below:
 }
 {% endhighlight %}
 
+This adapter is targeted for Elasticsearch 2.x. To use Calcite with Elasticsearch 5.x+ you can use the factory
+of the adapter targeted for Elasticsearch 5.x: `org.apache.calcite.adapter.elasticsearch5.Elasticsearch5SchemaFactory`
+
 Assuming this file is stored as `model.json`, you can connect to
 Elasticsearch via [`sqlline`](https://github.com/julianhyde/sqlline) as
 follows:

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/sqlline
----------------------------------------------------------------------
diff --git a/sqlline b/sqlline
index 6e4b4e9..b0fc489 100755
--- a/sqlline
+++ b/sqlline
@@ -37,7 +37,7 @@ if [ ! -f target/fullclasspath.txt ]; then
 fi
 
 CP=
-for module in core cassandra druid elasticsearch file mongodb spark splunk example/csv example/function; do
+for module in core cassandra druid elasticsearch2 elasticsearch5 file mongodb spark splunk example/csv example/function; do
   CP=${CP}${module}/target/classes:
   CP=${CP}${module}/target/test-classes:
 done

http://git-wip-us.apache.org/repos/asf/calcite/blob/e1525926/sqlline.bat
----------------------------------------------------------------------
diff --git a/sqlline.bat b/sqlline.bat
index 99548de..53f7159 100644
--- a/sqlline.bat
+++ b/sqlline.bat
@@ -23,6 +23,6 @@
 :: Copy dependency jars on first call. (To force jar refresh, remove target\dependencies)
 if not exist target\dependencies (call mvn -B dependency:copy-dependencies -DoverWriteReleases=false -DoverWriteSnapshots=false -DoverWriteIfNewer=true -DoutputDirectory=target\dependencies)
 
-java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;cassandra\target\dependencies\*;druid\target\dependencies\*;elasticsearch\target\dependencies\*;file\target\dependencies\*;mongodb\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
+java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;cassandra\target\dependencies\*;druid\target\dependencies\*;elasticsearch2\target\dependencies\*;elasticsearch5\target\dependencies\*;file\target\dependencies\*;mongodb\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
 
 :: End sqlline.bat