You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2015/03/12 09:16:42 UTC

[4/7] metamodel git commit: Added support for DELETE and thereby also simplified UPDATE statements on ElasticSearch. Fixes METAMODEL-79.

Added support for DELETE and thereby also simplified UPDATE statements
on ElasticSearch. Fixes METAMODEL-79.

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

Branch: refs/heads/master
Commit: 830d1c76530cb4129061fc6bb5335c3834c29c64
Parents: a93bd9c
Author: Kasper Sørensen <i....@gmail.com>
Authored: Sun Mar 8 16:05:10 2015 +0100
Committer: Kasper Sørensen <i....@gmail.com>
Committed: Sun Mar 8 16:05:10 2015 +0100

----------------------------------------------------------------------
 .../elasticsearch/ElasticSearchDataContext.java | 124 ++++++++++++++-
 .../elasticsearch/ElasticSearchDataSet.java     |  10 ++
 .../ElasticSearchDeleteBuilder.java             |  96 ++++++++++++
 .../ElasticSearchInsertBuilder.java             |  76 ++++++++++
 .../ElasticSearchInsertIntoBuilder.java         |  76 ----------
 .../ElasticSearchUpdateCallback.java            |  22 +--
 .../ElasticSearchDataContextTest.java           | 150 ++++++++++++++++++-
 7 files changed, 447 insertions(+), 107 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel/blob/830d1c76/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContext.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContext.java b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContext.java
index d465144..c2d7f1b 100644
--- a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContext.java
+++ b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContext.java
@@ -36,6 +36,8 @@ import org.apache.metamodel.data.DataSetHeader;
 import org.apache.metamodel.data.Row;
 import org.apache.metamodel.data.SimpleDataSetHeader;
 import org.apache.metamodel.query.FilterItem;
+import org.apache.metamodel.query.LogicalOperator;
+import org.apache.metamodel.query.OperatorType;
 import org.apache.metamodel.query.SelectItem;
 import org.apache.metamodel.schema.Column;
 import org.apache.metamodel.schema.MutableColumn;
@@ -43,6 +45,7 @@ import org.apache.metamodel.schema.MutableSchema;
 import org.apache.metamodel.schema.MutableTable;
 import org.apache.metamodel.schema.Schema;
 import org.apache.metamodel.schema.Table;
+import org.apache.metamodel.util.CollectionUtils;
 import org.apache.metamodel.util.SimpleTableDef;
 import org.elasticsearch.Version;
 import org.elasticsearch.action.admin.cluster.state.ClusterStateRequestBuilder;
@@ -58,6 +61,8 @@ import org.elasticsearch.common.collect.ImmutableOpenMap;
 import org.elasticsearch.common.hppc.ObjectLookupContainer;
 import org.elasticsearch.common.hppc.cursors.ObjectCursor;
 import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -250,17 +255,126 @@ public class ElasticSearchDataContext extends QueryPostprocessDataContext implem
     }
 
     @Override
+    protected DataSet materializeMainSchemaTable(Table table, List<SelectItem> selectItems,
+            List<FilterItem> whereItems, int firstRow, int maxRows) {
+        final QueryBuilder queryBuilder = createQueryBuilderForSimpleWhere(table, whereItems, LogicalOperator.AND);
+        if (queryBuilder != null) {
+            // where clause can be pushed down to an ElasticSearch query
+            final SearchRequestBuilder searchRequest = createSearchRequest(table, firstRow, maxRows, queryBuilder);
+            final SearchResponse response = searchRequest.execute().actionGet();
+            return new ElasticSearchDataSet(elasticSearchClient, response, selectItems, false);
+        }
+        return super.materializeMainSchemaTable(table, selectItems, whereItems, firstRow, maxRows);
+    }
+
+    @Override
     protected DataSet materializeMainSchemaTable(Table table, Column[] columns, int maxRows) {
+        final SearchRequestBuilder searchRequest = createSearchRequest(table, 1, maxRows, null);
+        final SearchResponse response = searchRequest.execute().actionGet();
+        return new ElasticSearchDataSet(elasticSearchClient, response, columns, false);
+    }
+
+    private SearchRequestBuilder createSearchRequest(Table table, int firstRow, int maxRows, QueryBuilder queryBuilder) {
         final String documentType = table.getName();
-        final SearchRequestBuilder requestBuilder = elasticSearchClient.prepareSearch(indexName).setTypes(documentType);
+        final SearchRequestBuilder searchRequest = elasticSearchClient.prepareSearch(indexName).setTypes(documentType);
+        if (firstRow > 1) {
+            final int zeroBasedFrom = firstRow - 1;
+            searchRequest.setFrom(zeroBasedFrom);
+        }
         if (limitMaxRowsIsSet(maxRows)) {
-            requestBuilder.setSize(maxRows);
+            searchRequest.setSize(maxRows);
         } else {
-            requestBuilder.setScroll(TIMEOUT_SCROLL);
+            searchRequest.setScroll(TIMEOUT_SCROLL);
         }
 
-        final SearchResponse response = requestBuilder.execute().actionGet();
-        return new ElasticSearchDataSet(elasticSearchClient, response, columns, false);
+        if (queryBuilder != null) {
+            searchRequest.setQuery(queryBuilder);
+        }
+
+        return searchRequest;
+    }
+
+    /**
+     * Creates, if possible, a {@link QueryBuilder} object which can be used to
+     * push down one or more {@link FilterItem}s to ElasticSearch's backend.
+     * 
+     * @param table
+     * @param whereItems
+     * @param logicalOperator
+     * @return a {@link QueryBuilder} if one was produced, or null if the items
+     *         could not be pushed down to an ElasticSearch query
+     */
+    protected QueryBuilder createQueryBuilderForSimpleWhere(Table table, List<FilterItem> whereItems,
+            LogicalOperator logicalOperator) {
+        if (whereItems.isEmpty()) {
+            return QueryBuilders.matchAllQuery();
+        }
+
+        List<QueryBuilder> children = new ArrayList<QueryBuilder>(whereItems.size());
+        for (FilterItem item : whereItems) {
+            final QueryBuilder itemQueryBuilder;
+
+            if (item.isCompoundFilter()) {
+                final List<FilterItem> childItems = Arrays.asList(item.getChildItems());
+                itemQueryBuilder = createQueryBuilderForSimpleWhere(table, childItems, item.getLogicalOperator());
+                if (itemQueryBuilder == null) {
+                    // something was not supported, so we have to forfeit here
+                    // too.
+                    return null;
+                }
+            } else {
+                final Column column = item.getSelectItem().getColumn();
+                if (column == null) {
+                    // unsupport type of where item - must have a column
+                    // reference
+                    return null;
+                }
+                final String fieldName = column.getName();
+                final Object operand = item.getOperand();
+                final OperatorType operator = item.getOperator();
+
+                switch (operator) {
+                case EQUALS_TO:
+                    itemQueryBuilder = QueryBuilders.termQuery(fieldName, operand);
+                    break;
+                case DIFFERENT_FROM:
+                    itemQueryBuilder = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(fieldName, operand));
+                    break;
+                case IN:
+                    final List<?> operands = CollectionUtils.toList(operand);
+                    itemQueryBuilder = QueryBuilders.termsQuery(fieldName, operands);
+                    break;
+                case LIKE:
+                case GREATER_THAN_OR_EQUAL:
+                case GREATER_THAN:
+                case LESS_THAN:
+                case LESS_THAN_OR_EQUAL:
+                default:
+                    // not (yet) support operator types
+                    return null;
+                }
+            }
+
+            children.add(itemQueryBuilder);
+        }
+
+        // just one where item - just return the child query builder
+        if (children.size() == 1) {
+            return children.get(0);
+        }
+
+        // build a bool query
+        final BoolQueryBuilder result = QueryBuilders.boolQuery();
+        for (QueryBuilder child : children) {
+            switch (logicalOperator) {
+            case AND:
+                result.must(child);
+            case OR:
+                result.should(child);
+            }
+        }
+
+        return result;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/metamodel/blob/830d1c76/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataSet.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataSet.java b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataSet.java
index e4f1054..8c41524 100644
--- a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataSet.java
+++ b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDataSet.java
@@ -18,12 +18,14 @@
  */
 package org.apache.metamodel.elasticsearch;
 
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.metamodel.data.AbstractDataSet;
 import org.apache.metamodel.data.DataSet;
 import org.apache.metamodel.data.Row;
+import org.apache.metamodel.query.SelectItem;
 import org.apache.metamodel.schema.Column;
 import org.elasticsearch.action.search.ClearScrollRequestBuilder;
 import org.elasticsearch.action.search.SearchResponse;
@@ -46,6 +48,14 @@ final class ElasticSearchDataSet extends AbstractDataSet {
     private SearchHit _currentHit;
     private int _hitIndex = 0;
 
+    public ElasticSearchDataSet(Client client, SearchResponse searchResponse, List<SelectItem> selectItems,
+            boolean queryPostProcessed) {
+        super(selectItems);
+        _client = client;
+        _searchResponse = searchResponse;
+        _closed = new AtomicBoolean(false);
+    }
+    
     public ElasticSearchDataSet(Client client, SearchResponse searchResponse, Column[] columns,
             boolean queryPostProcessed) {
         super(columns);

http://git-wip-us.apache.org/repos/asf/metamodel/blob/830d1c76/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDeleteBuilder.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDeleteBuilder.java b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDeleteBuilder.java
new file mode 100644
index 0000000..05d1f15
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchDeleteBuilder.java
@@ -0,0 +1,96 @@
+/**
+ * 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.metamodel.elasticsearch;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.delete.AbstractRowDeletionBuilder;
+import org.apache.metamodel.delete.RowDeletionBuilder;
+import org.apache.metamodel.query.FilterItem;
+import org.apache.metamodel.query.LogicalOperator;
+import org.apache.metamodel.schema.Table;
+import org.elasticsearch.action.deletebyquery.DeleteByQueryRequestBuilder;
+import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;
+import org.elasticsearch.action.deletebyquery.IndexDeleteByQueryResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link RowDeletionBuilder} implementation for
+ * {@link ElasticSearchDataContext}.
+ */
+final class ElasticSearchDeleteBuilder extends AbstractRowDeletionBuilder {
+
+    private static final Logger logger = LoggerFactory.getLogger(ElasticSearchDeleteBuilder.class);
+
+    private final ElasticSearchUpdateCallback _updateCallback;
+
+    public ElasticSearchDeleteBuilder(ElasticSearchUpdateCallback updateCallback, Table table) {
+        super(table);
+        _updateCallback = updateCallback;
+    }
+
+    @Override
+    public void execute() throws MetaModelException {
+        final Table table = getTable();
+        final String documentType = table.getName();
+
+        final ElasticSearchDataContext dataContext = _updateCallback.getDataContext();
+        final Client client = dataContext.getElasticSearchClient();
+        final String indexName = dataContext.getIndexName();
+
+        final DeleteByQueryRequestBuilder deleteByQueryRequestBuilder = new DeleteByQueryRequestBuilder(client);
+        deleteByQueryRequestBuilder.setIndices(indexName);
+        deleteByQueryRequestBuilder.setTypes(documentType);
+
+        final List<FilterItem> whereItems = getWhereItems();
+        if (whereItems.isEmpty()) {
+            // truncate the index
+            deleteByQueryRequestBuilder.setQuery(QueryBuilders.matchAllQuery());
+        } else {
+            // delete by query
+            final QueryBuilder queryBuilder = dataContext.createQueryBuilderForSimpleWhere(table, whereItems,
+                    LogicalOperator.AND);
+            if (queryBuilder == null) {
+                // TODO: The where items could not be pushed down to a query. We
+                // could solve this by running a query first, gather all
+                // document IDs and then delete by IDs.
+                throw new UnsupportedOperationException("Could not push down WHERE items to delete by query request: "
+                        + whereItems);
+            }
+            deleteByQueryRequestBuilder.setQuery(queryBuilder);
+        }
+
+        final DeleteByQueryResponse response = deleteByQueryRequestBuilder.execute().actionGet();
+
+        if (logger.isDebugEnabled()) {
+            final Map<String, Object> headers = response.getHeaders();
+            final IndexDeleteByQueryResponse indexResponse = response.getIndex(indexName);
+            final Map<String, Object> indexHeaders = indexResponse.getHeaders();
+
+            logger.debug("Deleted documents by query. Response headers: {}, Index headers: {}", headers, indexHeaders);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/830d1c76/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertBuilder.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertBuilder.java b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertBuilder.java
new file mode 100644
index 0000000..d4302a7
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertBuilder.java
@@ -0,0 +1,76 @@
+/**
+ * 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.metamodel.elasticsearch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.insert.AbstractRowInsertionBuilder;
+import org.apache.metamodel.schema.Column;
+import org.apache.metamodel.schema.Table;
+import org.elasticsearch.action.index.IndexRequestBuilder;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.client.Client;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ElasticSearchInsertBuilder extends AbstractRowInsertionBuilder<ElasticSearchUpdateCallback> {
+
+    private static final Logger logger = LoggerFactory.getLogger(ElasticSearchInsertBuilder.class);
+
+    public ElasticSearchInsertBuilder(ElasticSearchUpdateCallback updateCallback, Table table) {
+        super(updateCallback, table);
+    }
+
+    @Override
+    public void execute() throws MetaModelException {
+        final ElasticSearchDataContext dataContext = getUpdateCallback().getDataContext();
+        final Client client = dataContext.getElasticSearchClient();
+        final String indexName = dataContext.getIndexName();
+        final String documentType = getTable().getName();
+        final IndexRequestBuilder requestBuilder = new IndexRequestBuilder(client, indexName).setType(documentType);
+
+        final Map<String, Object> valueMap = new HashMap<String, Object>();
+        final Column[] columns = getColumns();
+        final Object[] values = getValues();
+        for (int i = 0; i < columns.length; i++) {
+            if (isSet(columns[i])) {
+                final String name = columns[i].getName();
+                final Object value = values[i];
+                if (ElasticSearchDataContext.FIELD_ID.equals(name)) {
+                    if (value != null) {
+                        requestBuilder.setId(value.toString());
+                    }
+                } else {
+                    valueMap.put(name, value);
+                }
+            }
+        }
+
+        assert !valueMap.isEmpty();
+
+        requestBuilder.setSource(valueMap);
+        requestBuilder.setCreate(true);
+
+        final IndexResponse result = requestBuilder.execute().actionGet();
+        logger.debug("Inserted document: id={}, created={}", result.getId(), result.isCreated());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/830d1c76/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertIntoBuilder.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertIntoBuilder.java b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertIntoBuilder.java
deleted file mode 100644
index 754624f..0000000
--- a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchInsertIntoBuilder.java
+++ /dev/null
@@ -1,76 +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.metamodel.elasticsearch;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.metamodel.MetaModelException;
-import org.apache.metamodel.insert.AbstractRowInsertionBuilder;
-import org.apache.metamodel.schema.Column;
-import org.apache.metamodel.schema.Table;
-import org.elasticsearch.action.index.IndexRequestBuilder;
-import org.elasticsearch.action.index.IndexResponse;
-import org.elasticsearch.client.Client;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-final class ElasticSearchInsertIntoBuilder extends AbstractRowInsertionBuilder<ElasticSearchUpdateCallback> {
-
-    private static final Logger logger = LoggerFactory.getLogger(ElasticSearchInsertIntoBuilder.class);
-
-    public ElasticSearchInsertIntoBuilder(ElasticSearchUpdateCallback updateCallback, Table table) {
-        super(updateCallback, table);
-    }
-
-    @Override
-    public void execute() throws MetaModelException {
-        final ElasticSearchDataContext dataContext = getUpdateCallback().getDataContext();
-        final Client client = dataContext.getElasticSearchClient();
-        final String indexName = dataContext.getIndexName();
-        final String documentType = getTable().getName();
-        final IndexRequestBuilder requestBuilder = new IndexRequestBuilder(client, indexName).setType(documentType);
-
-        final Map<String, Object> valueMap = new HashMap<String, Object>();
-        final Column[] columns = getColumns();
-        final Object[] values = getValues();
-        for (int i = 0; i < columns.length; i++) {
-            if (isSet(columns[i])) {
-                final String name = columns[i].getName();
-                final Object value = values[i];
-                if (ElasticSearchDataContext.FIELD_ID.equals(name)) {
-                    if (value != null) {
-                        requestBuilder.setId(value.toString());
-                    }
-                } else {
-                    valueMap.put(name, value);
-                }
-            }
-        }
-
-        assert !valueMap.isEmpty();
-
-        requestBuilder.setSource(valueMap);
-        requestBuilder.setCreate(true);
-
-        final IndexResponse result = requestBuilder.execute().actionGet();
-        logger.debug("Inserted document: id={}, created={}", result.getId(), result.isCreated());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/830d1c76/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchUpdateCallback.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchUpdateCallback.java b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchUpdateCallback.java
index e162cc6..a3c6629 100644
--- a/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchUpdateCallback.java
+++ b/elasticsearch/src/main/java/org/apache/metamodel/elasticsearch/ElasticSearchUpdateCallback.java
@@ -26,7 +26,6 @@ import org.apache.metamodel.drop.TableDropBuilder;
 import org.apache.metamodel.insert.RowInsertionBuilder;
 import org.apache.metamodel.schema.Schema;
 import org.apache.metamodel.schema.Table;
-import org.apache.metamodel.update.RowUpdationBuilder;
 import org.elasticsearch.client.Client;
 
 /**
@@ -63,33 +62,18 @@ final class ElasticSearchUpdateCallback extends AbstractUpdateCallback {
     @Override
     public RowInsertionBuilder insertInto(Table table) throws IllegalArgumentException, IllegalStateException,
             UnsupportedOperationException {
-        return new ElasticSearchInsertIntoBuilder(this, table);
-    }
-
-    @Override
-    public boolean isUpdateSupported() {
-        // TODO
-        return false;
-    }
-
-    @Override
-    public RowUpdationBuilder update(Table table) throws IllegalArgumentException, IllegalStateException,
-            UnsupportedOperationException {
-        // TODO
-        throw new UnsupportedOperationException();
+        return new ElasticSearchInsertBuilder(this, table);
     }
 
     @Override
     public boolean isDeleteSupported() {
-        // TODO
-        return false;
+        return true;
     }
 
     @Override
     public RowDeletionBuilder deleteFrom(Table table) throws IllegalArgumentException, IllegalStateException,
             UnsupportedOperationException {
-        // TODO
-        throw new UnsupportedOperationException();
+        return new ElasticSearchDeleteBuilder(this, table);
     }
 
     public void onExecuteUpdateFinished() {

http://git-wip-us.apache.org/repos/asf/metamodel/blob/830d1c76/elasticsearch/src/test/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContextTest.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContextTest.java b/elasticsearch/src/test/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContextTest.java
index 6544686..0c48391 100644
--- a/elasticsearch/src/test/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContextTest.java
+++ b/elasticsearch/src/test/java/org/apache/metamodel/elasticsearch/ElasticSearchDataContextTest.java
@@ -19,10 +19,7 @@
 package org.apache.metamodel.elasticsearch;
 
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 import java.util.Arrays;
 import java.util.Date;
@@ -32,14 +29,16 @@ import java.util.Map;
 
 import javax.swing.table.TableModel;
 
+import org.apache.metamodel.MetaModelHelper;
 import org.apache.metamodel.UpdateCallback;
 import org.apache.metamodel.UpdateScript;
 import org.apache.metamodel.UpdateableDataContext;
 import org.apache.metamodel.create.CreateTable;
 import org.apache.metamodel.data.DataSet;
 import org.apache.metamodel.data.DataSetTableModel;
-import org.apache.metamodel.data.FilteredDataSet;
 import org.apache.metamodel.data.InMemoryDataSet;
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.delete.DeleteFrom;
 import org.apache.metamodel.drop.DropTable;
 import org.apache.metamodel.elasticsearch.utils.EmbeddedElasticsearchServer;
 import org.apache.metamodel.query.FunctionType;
@@ -49,6 +48,7 @@ import org.apache.metamodel.schema.Column;
 import org.apache.metamodel.schema.ColumnType;
 import org.apache.metamodel.schema.Schema;
 import org.apache.metamodel.schema.Table;
+import org.apache.metamodel.update.Update;
 import org.elasticsearch.action.bulk.BulkRequestBuilder;
 import org.elasticsearch.client.Client;
 import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -206,7 +206,8 @@ public class ElasticSearchDataContextTest {
 
         final Column fooColumn = table.getColumnByName("foo");
         final Column idColumn = table.getPrimaryKeys()[0];
-        assertEquals("Column[name=_id,columnNumber=0,type=STRING,nullable=null,nativeType=null,columnSize=null]", idColumn.toString());
+        assertEquals("Column[name=_id,columnNumber=0,type=STRING,nullable=null,nativeType=null,columnSize=null]",
+                idColumn.toString());
 
         dataContext.executeUpdate(new UpdateScript() {
             @Override
@@ -233,6 +234,126 @@ public class ElasticSearchDataContextTest {
     }
 
     @Test
+    public void testDeleteAll() throws Exception {
+        final Schema schema = dataContext.getDefaultSchema();
+        final CreateTable createTable = new CreateTable(schema, "testCreateTable");
+        createTable.withColumn("foo").ofType(ColumnType.STRING);
+        createTable.withColumn("bar").ofType(ColumnType.NUMBER);
+        dataContext.executeUpdate(createTable);
+
+        final Table table = schema.getTableByName("testCreateTable");
+
+        dataContext.executeUpdate(new UpdateScript() {
+            @Override
+            public void run(UpdateCallback callback) {
+                callback.insertInto(table).value("foo", "hello").value("bar", 42).execute();
+                callback.insertInto(table).value("foo", "world").value("bar", 43).execute();
+            }
+        });
+
+        dataContext.executeUpdate(new DeleteFrom(table));
+
+        Row row = MetaModelHelper.executeSingleRowQuery(dataContext, dataContext.query().from(table).selectCount()
+                .toQuery());
+        assertEquals("Row[values=[0]]", row.toString());
+
+        dataContext.executeUpdate(new DropTable(table));
+    }
+
+    @Test
+    public void testDeleteByQuery() throws Exception {
+        final Schema schema = dataContext.getDefaultSchema();
+        final CreateTable createTable = new CreateTable(schema, "testCreateTable");
+        createTable.withColumn("foo").ofType(ColumnType.STRING);
+        createTable.withColumn("bar").ofType(ColumnType.NUMBER);
+        dataContext.executeUpdate(createTable);
+
+        final Table table = schema.getTableByName("testCreateTable");
+
+        dataContext.executeUpdate(new UpdateScript() {
+            @Override
+            public void run(UpdateCallback callback) {
+                callback.insertInto(table).value("foo", "hello").value("bar", 42).execute();
+                callback.insertInto(table).value("foo", "world").value("bar", 43).execute();
+            }
+        });
+
+        dataContext.executeUpdate(new DeleteFrom(table).where("foo").eq("hello").where("bar").eq(42));
+
+        Row row = MetaModelHelper.executeSingleRowQuery(dataContext,
+                dataContext.query().from(table).select("foo", "bar").toQuery());
+        assertEquals("Row[values=[world, 43]]", row.toString());
+
+        dataContext.executeUpdate(new DropTable(table));
+    }
+
+    @Test
+    public void testDeleteUnsupportedQueryType() throws Exception {
+        final Schema schema = dataContext.getDefaultSchema();
+        final CreateTable createTable = new CreateTable(schema, "testCreateTable");
+        createTable.withColumn("foo").ofType(ColumnType.STRING);
+        createTable.withColumn("bar").ofType(ColumnType.NUMBER);
+        dataContext.executeUpdate(createTable);
+
+        final Table table = schema.getTableByName("testCreateTable");
+        try {
+
+            dataContext.executeUpdate(new UpdateScript() {
+                @Override
+                public void run(UpdateCallback callback) {
+                    callback.insertInto(table).value("foo", "hello").value("bar", 42).execute();
+                    callback.insertInto(table).value("foo", "world").value("bar", 43).execute();
+                }
+            });
+
+            // greater than is not yet supported
+            try {
+                dataContext.executeUpdate(new DeleteFrom(table).where("bar").gt(40));
+                fail("Exception expected");
+            } catch (UnsupportedOperationException e) {
+                assertEquals("Could not push down WHERE items to delete by query request: [testCreateTable.bar > 40]",
+                        e.getMessage());
+            }
+
+        } finally {
+            dataContext.executeUpdate(new DropTable(table));
+        }
+    }
+
+    @Test
+    public void testUpdateRow() throws Exception {
+        final Schema schema = dataContext.getDefaultSchema();
+        final CreateTable createTable = new CreateTable(schema, "testCreateTable");
+        createTable.withColumn("foo").ofType(ColumnType.STRING);
+        createTable.withColumn("bar").ofType(ColumnType.NUMBER);
+        dataContext.executeUpdate(createTable);
+
+        final Table table = schema.getTableByName("testCreateTable");
+        try {
+
+            dataContext.executeUpdate(new UpdateScript() {
+                @Override
+                public void run(UpdateCallback callback) {
+                    callback.insertInto(table).value("foo", "hello").value("bar", 42).execute();
+                    callback.insertInto(table).value("foo", "world").value("bar", 43).execute();
+                }
+            });
+
+            dataContext.executeUpdate(new Update(table).value("foo", "howdy").where("bar").eq(42));
+
+            DataSet dataSet = dataContext.query().from(table).select("foo", "bar").orderBy("bar").execute();
+            assertTrue(dataSet.next());
+            assertEquals("Row[values=[howdy, 42]]", dataSet.getRow().toString());
+            assertTrue(dataSet.next());
+            assertEquals("Row[values=[world, 43]]", dataSet.getRow().toString());
+            assertFalse(dataSet.next());
+            dataSet.close();
+        } finally {
+            dataContext.executeUpdate(new DropTable(table));
+        }
+    }
+
+    @Test
     public void testDropTable() throws Exception {
         Table table = dataContext.getDefaultSchema().getTableByName(peopleIndexType);
 
@@ -262,7 +383,22 @@ public class ElasticSearchDataContextTest {
     public void testWhereColumnEqualsValues() throws Exception {
         DataSet ds = dataContext.query().from(bulkIndexType).select("user").and("message").where("user")
                 .isEquals("user4").execute();
-        assertEquals(FilteredDataSet.class, ds.getClass());
+        assertEquals(ElasticSearchDataSet.class, ds.getClass());
+
+        try {
+            assertTrue(ds.next());
+            assertEquals("Row[values=[user4, 4]]", ds.getRow().toString());
+            assertFalse(ds.next());
+        } finally {
+            ds.close();
+        }
+    }
+
+    @Test
+    public void testWhereMultiColumnsEqualValues() throws Exception {
+        DataSet ds = dataContext.query().from(bulkIndexType).select("user").and("message").where("user")
+                .isEquals("user4").and("message").ne(5).execute();
+        assertEquals(ElasticSearchDataSet.class, ds.getClass());
 
         try {
             assertTrue(ds.next());