Merge remote-tracking branch 'jhorcicka/bug/dc-2199-schema-equals-column'
diff --git a/elasticsearch/common/src/main/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtils.java b/elasticsearch/common/src/main/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtils.java
index 4a55121..a321e6f 100644
--- a/elasticsearch/common/src/main/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtils.java
+++ b/elasticsearch/common/src/main/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtils.java
@@ -225,12 +225,16 @@
         if (OperatorType.EQUALS_TO.equals(filterItem.getOperator())) {
             if (filterItem.getOperand() == null) {
                 return getMissingQuery(column.getName());
+            } else if (column.getType().isLiteral() && filterItem.getOperand().equals("")) {
+                return QueryBuilders.boolQuery().mustNot(QueryBuilders.wildcardQuery(column.getName(), "?*"));
             } else {
                 return matchOrTermQuery(column, filterItem.getOperand());
             }
         } else if (OperatorType.DIFFERENT_FROM.equals(filterItem.getOperator())) {
             if (filterItem.getOperand() == null) {
                 return getExistsQuery(column.getName());
+            } else if (column.getType().isLiteral() && filterItem.getOperand().equals("")) {
+                return QueryBuilders.boolQuery().must(QueryBuilders.wildcardQuery(column.getName(), "?*"));
             } else {
                 return QueryBuilders.boolQuery().mustNot(matchOrTermQuery(column, filterItem.getOperand()));
             }
@@ -248,7 +252,7 @@
     }
 
     private static QueryBuilder matchOrTermQuery(final Column column, final Object operand) {
-        if (column.getType().isLiteral()) {
+        if (column.getType().isLiteral() && operand != null && !operand.equals("")) {
             return QueryBuilders.matchQuery(column.getName(), operand);
         } else {
             return QueryBuilders.termQuery(column.getName(), operand);
diff --git a/elasticsearch/common/src/test/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtilsTest.java b/elasticsearch/common/src/test/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtilsTest.java
index 923ae9e..30a867a 100644
--- a/elasticsearch/common/src/test/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtilsTest.java
+++ b/elasticsearch/common/src/test/java/org/apache/metamodel/elasticsearch/common/ElasticSearchUtilsTest.java
@@ -41,7 +41,7 @@
 
 public class ElasticSearchUtilsTest extends TestCase {
 
-    public void testAssignDocumentIdForPrimaryKeys() throws Exception {
+    public void testAssignDocumentIdForPrimaryKeys() {
         MutableColumn primaryKeyColumn = new MutableColumn("value1", ColumnType.STRING).setPrimaryKey(true);
         SelectItem primaryKeyItem = new SelectItem(primaryKeyColumn);
         List<SelectItem> selectItems1 = Collections.singletonList(primaryKeyItem);
@@ -55,7 +55,7 @@
         assertEquals(primaryKeyValue, documentId);
     }
 
-    public void testCreateRowWithParsableDates() throws Exception {
+    public void testCreateRowWithParsableDates() {
         SelectItem item1 = new SelectItem(new MutableColumn("value1", ColumnType.STRING));
         SelectItem item2 = new SelectItem(new MutableColumn("value2", ColumnType.DATE));
         List<SelectItem> selectItems1 = Arrays.asList(item1, item2);
diff --git a/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/ElasticSearchRestDataContextIT.java b/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/ElasticSearchRestDataContextIT.java
index 7f83723..879fa3f 100644
--- a/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/ElasticSearchRestDataContextIT.java
+++ b/elasticsearch/rest/src/test/java/org/apache/metamodel/elasticsearch/rest/ElasticSearchRestDataContextIT.java
@@ -21,7 +21,13 @@
 import static org.apache.metamodel.elasticsearch.rest.ElasticSearchRestDataContext.DEFAULT_TABLE_NAME;
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.net.URI;
@@ -48,7 +54,9 @@
 import org.apache.metamodel.delete.DeleteFrom;
 import org.apache.metamodel.elasticsearch.common.ElasticSearchUtils;
 import org.apache.metamodel.insert.InsertInto;
+import org.apache.metamodel.query.FilterItem;
 import org.apache.metamodel.query.FunctionType;
+import org.apache.metamodel.query.OperatorType;
 import org.apache.metamodel.query.Query;
 import org.apache.metamodel.query.SelectItem;
 import org.apache.metamodel.query.parser.QueryParserException;
@@ -60,12 +68,12 @@
 import org.apache.metamodel.schema.Table;
 import org.apache.metamodel.schema.TableType;
 import org.apache.metamodel.update.Update;
+import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
 import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
 import org.elasticsearch.action.bulk.BulkRequest;
 import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.RestHighLevelClient;
-import org.elasticsearch.client.indices.CreateIndexRequest;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.junit.After;
 import org.junit.Before;
@@ -119,11 +127,65 @@
         indexOnePeopleDocument("male", 17, 2);
         indexOnePeopleDocument("male", 18, 3);
         indexOnePeopleDocument("male", 18, 4);
+        indexOnePeopleDocument("", 24, 12);
+        indexOnePeopleDocument(null, 25, 13);
 
         dataContext.refreshSchemas();
     }
 
     @Test
+    public void testNullAndNotNull() throws IOException {
+        insertPeopleDocuments();
+        final Table table = dataContext.getDefaultSchema().getTableByName(DEFAULT_TABLE_NAME);
+        final Column column = table.getColumnByName("gender");
+
+        final FilterItem nullFilterItem = new FilterItem(new SelectItem(column), OperatorType.EQUALS_TO, null);
+        final FilterItem notNullFilterItem = new FilterItem(new SelectItem(column), OperatorType.DIFFERENT_FROM, null);
+
+        final Query nullQuery =
+                dataContext.query().from(DEFAULT_TABLE_NAME).selectCount().where(nullFilterItem).toQuery();
+        final Query notNullQuery =
+                dataContext.query().from(DEFAULT_TABLE_NAME).selectCount().where(notNullFilterItem).toQuery();
+        final Query allQuery = dataContext.query().from(DEFAULT_TABLE_NAME).selectCount().toQuery();
+
+        final int nullCount =
+                ((Number) MetaModelHelper.executeSingleRowQuery(dataContext, nullQuery).getValue(0)).intValue();
+        final int notNullCount =
+                ((Number) MetaModelHelper.executeSingleRowQuery(dataContext, notNullQuery).getValue(0)).intValue();
+        final int allCount =
+                ((Number) MetaModelHelper.executeSingleRowQuery(dataContext, allQuery).getValue(0)).intValue();
+
+        assertEquals(nullCount, 1);
+        assertEquals(allCount, nullCount + notNullCount);
+    }
+
+    @Test
+    public void testEmptyAndNotEmpty() throws IOException {
+        insertPeopleDocuments();
+        final Table table = dataContext.getDefaultSchema().getTableByName(DEFAULT_TABLE_NAME);
+        final Column column = table.getColumnByName("gender");
+
+        final FilterItem emptyFilterItem = new FilterItem(new SelectItem(column), OperatorType.EQUALS_TO, "");
+        final FilterItem notEmptyFilterItem = new FilterItem(new SelectItem(column), OperatorType.DIFFERENT_FROM, "");
+
+        final Query emptyQuery =
+                dataContext.query().from(DEFAULT_TABLE_NAME).selectCount().where(emptyFilterItem).toQuery();
+        final Query notEmptyQuery =
+                dataContext.query().from(DEFAULT_TABLE_NAME).selectCount().where(notEmptyFilterItem).toQuery();
+        final Query allQuery = dataContext.query().from(DEFAULT_TABLE_NAME).selectCount().toQuery();
+
+        final int emptyCount =
+                ((Number) MetaModelHelper.executeSingleRowQuery(dataContext, emptyQuery).getValue(0)).intValue();
+        final int notEmptyCount =
+                ((Number) MetaModelHelper.executeSingleRowQuery(dataContext, notEmptyQuery).getValue(0)).intValue();
+        final int allCount =
+                ((Number) MetaModelHelper.executeSingleRowQuery(dataContext, allQuery).getValue(0)).intValue();
+
+        assertEquals(emptyCount, 1);
+        assertEquals(allCount, emptyCount + notEmptyCount);
+    }
+
+    @Test
     public void testSimpleQuery() throws Exception {
         indexTweeterDocument(1);
 
@@ -490,13 +552,16 @@
 
         final Query query = new Query();
         query.from(table);
-        query.groupBy(table.getColumnByName("gender"));
+        final Column genderColumn = table.getColumnByName("gender");
+        query.groupBy(genderColumn);
         query
-                .select(new SelectItem(table.getColumnByName("gender")), new SelectItem(FunctionType.MAX, table
+                .select(new SelectItem(genderColumn), new SelectItem(FunctionType.MAX, table
                         .getColumnByName("age")), new SelectItem(FunctionType.MIN, table.getColumnByName("age")),
                         new SelectItem(FunctionType.COUNT, "*", "total"), new SelectItem(FunctionType.MIN, table
                                 .getColumnByName("id")).setAlias("firstId"));
-        query.orderBy("gender");
+        query.where(new FilterItem(new SelectItem(genderColumn), OperatorType.DIFFERENT_FROM, null));
+        query.where(new FilterItem(new SelectItem(genderColumn), OperatorType.DIFFERENT_FROM, ""));
+        query.orderBy(genderColumn);
         final DataSet data = dataContext.executeQuery(query);
         assertEquals("[" + DEFAULT_TABLE_NAME + ".gender, MAX(" + DEFAULT_TABLE_NAME + ".age), MIN("
                 + DEFAULT_TABLE_NAME + ".age), COUNT(*) AS total, MIN(" + DEFAULT_TABLE_NAME + ".id) AS firstId]",