This is an automated email from the ASF dual-hosted git repository.

fortino pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 5e5fe8b  OAK-9647: oak-search-elastic: nullCheckEnabled and 
notNullCheckEnabled queries not working as expected (#444)
5e5fe8b is described below

commit 5e5fe8b88d3ba218a43e12a9800fb871d5eaba25
Author: Fabrizio Fortino <[email protected]>
AuthorDate: Thu Dec 23 16:14:02 2021 +0100

    OAK-9647: oak-search-elastic: nullCheckEnabled and notNullCheckEnabled 
queries not working as expected (#444)
    
    * OAK-9647: remove duplicate (unused) constants
    
    * OAK-9647: remove duplicate propertyExistenceQuery, already present in 
common tests
    
    * OAK-9647: remove unneeded not null check on index definition
    
    * OAK-9647: no need to index nullProps and notNullProps in elastic. Move 
propertyExistence test in common tests
    
    * OAK-9647: handle notNull and null queries with exist/mustNotExist
    
    * OAK-9647: improved tests
---
 .../plugins/index/lucene/LuceneIndexConstants.java | 12 ----
 .../lucene/LucenePropertyIndexCommonTest.java      |  9 +++
 .../index/lucene/LucenePropertyIndexTest.java      | 61 -----------------
 .../index/elastic/index/ElasticDocument.java       | 23 +------
 .../index/elastic/index/ElasticDocumentMaker.java  |  4 +-
 .../index/elastic/query/ElasticRequestHandler.java | 11 +---
 .../elastic/util/TermQueryBuilderFactory.java      | 22 +------
 .../index/elastic/ElasticPropertyIndexTest.java    | 14 ----
 .../jackrabbit/oak/plugins/index/IndexOptions.java |  6 +-
 .../oak/plugins/index/PropertyIndexCommonTest.java | 77 +++++++++++++++++++++-
 10 files changed, 99 insertions(+), 140 deletions(-)

diff --git 
a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
 
b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
index fcc8144..b0713b1 100644
--- 
a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
+++ 
b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
@@ -142,18 +142,6 @@ public interface LuceneIndexConstants extends 
FulltextIndexConstants {
     String PROP_USE_IN_SIMILARITY = "useInSimilarity";
 
     /**
-     * Property definition config indicating that null check support should be
-     * enabled for this property
-     */
-    String PROP_NULL_CHECK_ENABLED = "nullCheckEnabled";
-
-    /**
-     * Property definition config indicating that this property would be used 
with
-     * 'IS NOT NULL' constraint
-     */
-    String PROP_NOT_NULL_CHECK_ENABLED = "notNullCheckEnabled";
-
-    /**
      * IndexRule level config to indicate that Node name should also be index
      * to support fn:name() queries
      */
diff --git 
a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexCommonTest.java
 
b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexCommonTest.java
index 611951a..af1064b 100644
--- 
a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexCommonTest.java
+++ 
b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexCommonTest.java
@@ -50,4 +50,13 @@ public class LucenePropertyIndexCommonTest extends 
PropertyIndexCommonTest {
         executorService.shutdown();
     }
 
+    @Override
+    protected String propertyExistenceQueryWithNullCheckExpectedExplain() {
+        return "lucene:test1(/oak:index/test1) :notNullProps:propa";
+    }
+
+    @Override
+    protected String propertyNonExistenceQueryExpectedExplain() {
+        return "lucene:test1(/oak:index/test1) :nullProps:propa";
+    }
 }
diff --git 
a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
 
b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
index 8a045c2..a9f2969 100644
--- 
a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
+++ 
b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
@@ -592,21 +592,6 @@ public class LucenePropertyIndexTest extends 
AbstractQueryTest {
     }
 
     @Test
-    public void propertyExistenceQuery() throws Exception {
-        Tree idx = createIndex("test1", of("propa", "propb"));
-        idx.addChild(PROP_NODE).addChild("propa");
-        root.commit();
-
-        Tree test = root.getTree("/").addChild("test");
-        test.addChild("a").setProperty("propa", "a");
-        test.addChild("b").setProperty("propa", "c");
-        test.addChild("c").setProperty("propb", "e");
-        root.commit();
-
-        assertQuery("select [jcr:path] from [nt:base] where propa is not 
null", asList("/test/a", "/test/b"));
-    }
-
-    @Test
     public void explainScoreTest() throws Exception {
         Tree idx = createIndex("test1", of("propa"));
         idx.addChild(PROP_NODE).addChild("propa");
@@ -709,52 +694,6 @@ public class LucenePropertyIndexTest extends 
AbstractQueryTest {
         assertThat(explain(q), not(containsString("[content].[tags] is not 
null")));
     }
 
-    @Test
-    public void propertyExistenceQuery2() throws Exception {
-        NodeTypeRegistry.register(root, 
IOUtils.toInputStream(TestUtil.TEST_NODE_TYPE), "test nodeType");
-
-        Tree idx = createIndex("test1", of("propa", "propb"));
-        Tree props = TestUtil.newRulePropTree(idx, TestUtil.NT_TEST);
-        Tree prop = props.addChild(TestUtil.unique("prop"));
-        prop.setProperty(PROP_NAME, "propa");
-        prop.setProperty(PROP_PROPERTY_INDEX, true);
-        prop.setProperty(LuceneIndexConstants.PROP_NOT_NULL_CHECK_ENABLED, 
true);
-        root.commit();
-
-        Tree test = root.getTree("/").addChild("test");
-        createNodeWithType(test, "a", "oak:TestNode").setProperty("propa", 
"a");
-        createNodeWithType(test, "b", "oak:TestNode").setProperty("propa", 
"c");
-        createNodeWithType(test, "c", "oak:TestNode").setProperty("propb", 
"e");
-        root.commit();
-
-        String propabQuery = "select [jcr:path] from [oak:TestNode] where 
[propa] is not null";
-        assertThat(explain(propabQuery), 
containsString("lucene:test1(/oak:index/test1) :notNullProps:propa"));
-        assertQuery(propabQuery, asList("/test/a", "/test/b"));
-    }
-
-    @Test
-    public void propertyNonExistenceQuery() throws Exception {
-        NodeTypeRegistry.register(root, 
IOUtils.toInputStream(TestUtil.TEST_NODE_TYPE), "test nodeType");
-
-        Tree idx = createIndex("test1", of("propa", "propb"));
-        Tree props = TestUtil.newRulePropTree(idx, TestUtil.NT_TEST);
-        Tree prop = props.addChild(TestUtil.unique("prop"));
-        prop.setProperty(PROP_NAME, "propa");
-        prop.setProperty(PROP_PROPERTY_INDEX, true);
-        prop.setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, true);
-        root.commit();
-
-        Tree test = root.getTree("/").addChild("test");
-        createNodeWithType(test, "a", "oak:TestNode").setProperty("propa", 
"a");
-        createNodeWithType(test, "b", "oak:TestNode").setProperty("propa", 
"c");
-        createNodeWithType(test, "c", "oak:TestNode").setProperty("propb", 
"e");
-        root.commit();
-
-        String propabQuery = "select [jcr:path] from [oak:TestNode] where 
[propa] is null";
-        assertThat(explain(propabQuery), 
containsString("lucene:test1(/oak:index/test1) :nullProps:propa"));
-        assertQuery(propabQuery, asList("/test/c"));
-    }
-
     private static Tree createNodeWithType(Tree t, String nodeName, String 
typeName){
         t = t.addChild(nodeName);
         t.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName, Type.NAME);
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
index 07daf0f..bc66ef9 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
@@ -44,8 +44,6 @@ public class ElasticDocument {
     private final Set<String> fulltext;
     private final Set<String> suggest;
     private final Set<String> spellcheck;
-    private final List<String> notNullProps;
-    private final List<String> nullProps;
     private final Map<String, List<Object>> properties;
     private final Map<String, Object> similarityFields;
     private final Map<String, Map<String, Double>> dynamicBoostFields;
@@ -56,8 +54,6 @@ public class ElasticDocument {
         this.fulltext = new LinkedHashSet<>();
         this.suggest = new LinkedHashSet<>();
         this.spellcheck = new LinkedHashSet<>();
-        this.notNullProps = new ArrayList<>();
-        this.nullProps = new ArrayList<>();
         this.properties = new HashMap<>();
         this.similarityFields = new HashMap<>();
         this.dynamicBoostFields = new HashMap<>();
@@ -80,14 +76,6 @@ public class ElasticDocument {
         spellcheck.add(value);
     }
 
-    void notNullProp(String propName) {
-        notNullProps.add(propName);
-    }
-
-    void nullProp(String propName) {
-        nullProps.add(propName);
-    }
-
     // ES for String values (that are not interpreted as date or numbers etc) 
would analyze in the same
     // field and would index a sub-field "keyword" for non-analyzed value.
     // ref: https://www.elastic.co/blog/strings-are-dead-long-live-strings
@@ -138,12 +126,6 @@ public class ElasticDocument {
                 if (spellcheck.size() > 0) {
                     builder.field(FieldNames.SPELLCHECK, spellcheck);
                 }
-                if (notNullProps.size() > 0) {
-                    builder.field(FieldNames.NOT_NULL_PROPS, notNullProps);
-                }
-                if (nullProps.size() > 0) {
-                    builder.field(FieldNames.NULL_PROPS, nullProps);
-                }
                 for (Map.Entry<String, Object> simProp: 
similarityFields.entrySet()) {
                     builder.field(simProp.getKey(), simProp.getValue());
                 }
@@ -168,9 +150,8 @@ public class ElasticDocument {
 
             ret = Strings.toString(builder);
         } catch (IOException e) {
-            LOG.error("Error serializing document - path: {}, properties: {}, 
fulltext: {}, suggest: {}, " +
-                            "notNullProps: {}, nullProps: {}",
-                    path, properties, fulltext, suggest, notNullProps, 
nullProps, e);
+            LOG.error("Error serializing document - path: {}, properties: {}, 
fulltext: {}, suggest: {}, ",
+                    path, properties, fulltext, suggest, e);
             ret = null;
         }
 
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
index d71bc3f..86a9698 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
@@ -166,12 +166,12 @@ public class ElasticDocumentMaker extends 
FulltextDocumentMaker<ElasticDocument>
 
     @Override
     protected void indexNotNullProperty(ElasticDocument doc, 
PropertyDefinition pd) {
-        doc.notNullProp(pd.name);
+        // Elastic support exist queries for specific fields
     }
 
     @Override
     protected void indexNullProperty(ElasticDocument doc, PropertyDefinition 
pd) {
-        doc.nullProp(pd.name);
+        // Elastic support not exist queries for specific fields
     }
 
     @Override
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
index 1ef0ce3..24c8fac 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
@@ -103,8 +103,6 @@ import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuil
 import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newDepthQuery;
 import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newMixinTypeQuery;
 import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newNodeTypeQuery;
-import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newNotNullPropQuery;
-import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newNullPropQuery;
 import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newPathQuery;
 import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newPrefixPathQuery;
 import static 
org.apache.jackrabbit.oak.plugins.index.elastic.util.TermQueryBuilderFactory.newPrefixQuery;
@@ -796,13 +794,10 @@ public class ElasticRequestHandler {
         int propType = FulltextIndex.determinePropertyType(defn, pr);
 
         if (pr.isNullRestriction()) {
-            return newNullPropQuery(defn.name);
+            return 
QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(propertyName));
         }
-
-        //If notNullCheckEnabled explicitly enabled use the simple TermQuery
-        //otherwise later fallback to range query
-        if (pr.isNotNullRestriction() && defn.notNullCheckEnabled) {
-            return newNotNullPropQuery(defn.name);
+        if (pr.isNotNullRestriction()) {
+            return QueryBuilders.existsQuery(propertyName);
         }
 
         final String field = 
elasticIndexDefinition.getElasticKeyword(propertyName);
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/util/TermQueryBuilderFactory.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/util/TermQueryBuilderFactory.java
index 9674786..352318c 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/util/TermQueryBuilderFactory.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/util/TermQueryBuilderFactory.java
@@ -22,7 +22,6 @@ import 
org.apache.jackrabbit.oak.plugins.index.search.FieldNames;
 import 
org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndexPlanner;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.elasticsearch.index.query.BoolQueryBuilder;
-import org.elasticsearch.index.query.ExistsQueryBuilder;
 import org.elasticsearch.index.query.PrefixQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
@@ -51,10 +50,6 @@ public class TermQueryBuilderFactory {
     private TermQueryBuilderFactory() {
     }
 
-    private static ExistsQueryBuilder newExistsQuery(String field) {
-        return QueryBuilders.existsQuery(field);
-    }
-
     public static PrefixQueryBuilder newPrefixQuery(String field, @NotNull 
String value) {
         return prefixQuery(field, value);
     }
@@ -92,14 +87,6 @@ public class TermQueryBuilderFactory {
         return termQuery(JCR_MIXINTYPES, type);
     }
 
-    public static TermQueryBuilder newNotNullPropQuery(String propName) {
-        return termQuery(FieldNames.NOT_NULL_PROPS, propName);
-    }
-
-    public static TermQueryBuilder newNullPropQuery(String propName) {
-        return termQuery(FieldNames.NULL_PROPS, propName);
-    }
-
     private static <R> RangeQueryBuilder newRangeQuery(String field,
                                                        R first, R last, 
boolean firstIncluding, boolean lastIncluding) {
         return QueryBuilders.rangeQuery(field)
@@ -128,20 +115,17 @@ public class TermQueryBuilderFactory {
         } else if (pr.first != null && pr.last != null) {
             return newRangeQuery(propertyName, first, last,
                     pr.firstIncluding, pr.lastIncluding);
-        } else if (pr.first != null && pr.last == null) {
+        } else if (pr.first != null) {
             // '>' & '>=' use cases
             return newRangeQuery(propertyName, first, null, pr.firstIncluding, 
true);
-        } else if (pr.last != null && !pr.last.equals(pr.first)) {
+        } else if (pr.last != null) {
             // '<' & '<='
             return newRangeQuery(propertyName, null, last, true, 
pr.lastIncluding);
         } else if (pr.list != null) {
             return newInQuery(propertyName, pr.list.stream()
                     .map(propToObj)
                     .collect(Collectors.toList()));
-        } else if (pr.isNotNullRestriction()) {
-            // not null. For date lower bound of zero can be used
-            return newExistsQuery(propertyName);
-        } else {
+        }  else {
             return null;
         }
     }
diff --git 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
index 3325d85..26c10d3 100644
--- 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
+++ 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
@@ -140,18 +140,4 @@ public class ElasticPropertyIndexTest extends 
ElasticAbstractQueryTest {
                 containsString("elasticsearch:test1")));
     }
 
-    @Test
-    public void propertyExistenceQuery() throws Exception {
-        setIndex("test1", createIndex("propa", "propb"));
-
-        Tree test = root.getTree("/").addChild("test");
-        test.addChild("a").setProperty("propa", "a");
-        test.addChild("b").setProperty("propa", "c");
-        test.addChild("c").setProperty("propb", "e");
-        root.commit();
-
-        assertEventually(() -> assertQuery("select [jcr:path] from [nt:base] 
where propa is not null",
-                Arrays.asList("/test/a", "/test/b")));
-    }
-
 }
diff --git 
a/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexOptions.java
 
b/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexOptions.java
index fdf6ffc..22d8f84 100644
--- 
a/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexOptions.java
+++ 
b/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexOptions.java
@@ -44,10 +44,14 @@ public abstract class IndexOptions {
     }
 
     protected IndexDefinitionBuilder createIndex(IndexDefinitionBuilder 
builder, boolean isAsync, String... propNames) {
+        return createIndex(builder, "nt:base", isAsync, propNames);
+    }
+
+    protected IndexDefinitionBuilder createIndex(IndexDefinitionBuilder 
builder, String type, boolean isAsync, String... propNames) {
         if (!isAsync) {
             builder = builder.noAsync();
         }
-        IndexDefinitionBuilder.IndexRule indexRule = 
builder.indexRule("nt:base");
+        IndexDefinitionBuilder.IndexRule indexRule = builder.indexRule(type);
         for (String propName : propNames) {
             indexRule.property(propName).propertyIndex();
         }
diff --git 
a/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/PropertyIndexCommonTest.java
 
b/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/PropertyIndexCommonTest.java
index 3bc1126..8ea99ec 100644
--- 
a/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/PropertyIndexCommonTest.java
+++ 
b/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/PropertyIndexCommonTest.java
@@ -16,18 +16,26 @@
  */
 package org.apache.jackrabbit.oak.plugins.index;
 
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
 import 
org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.NodeTypeRegistry;
 import org.apache.jackrabbit.oak.query.AbstractQueryTest;
 import org.junit.Test;
 
 import java.util.Arrays;
 
+import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
 import static javax.jcr.PropertyType.TYPENAME_DATE;
 import static 
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROPDEF_PROP_NODE_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_NOT_NULL_CHECK_ENABLED;
+import static 
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_NULL_CHECK_ENABLED;
+import static 
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_PROPERTY_INDEX;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -159,7 +167,8 @@ public abstract class PropertyIndexCommonTest extends 
AbstractQueryTest {
 
     @Test
     public void propertyExistenceQuery() throws Exception {
-        indexOptions.setIndex(root, "test1", 
indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false, 
"propa", "propb"));
+        indexOptions.setIndex(root, "test1", 
indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(),
+                false, "propa", "propb"));
         root.commit();
 
         Tree test = root.getTree("/").addChild("test");
@@ -172,6 +181,64 @@ public abstract class PropertyIndexCommonTest extends 
AbstractQueryTest {
     }
 
     @Test
+    public void propertyExistenceQueryWithNullCheck() throws Exception {
+        NodeTypeRegistry.register(root, 
IOUtils.toInputStream(TestUtil.TEST_NODE_TYPE), "test nodeType");
+
+        Tree idx = indexOptions.setIndex(root, "test1",
+                
indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), 
TestUtil.NT_TEST, false, "propa", "propb"));
+        Tree props = TestUtil.newRulePropTree(idx, TestUtil.NT_TEST);
+        Tree prop = props.addChild(TestUtil.unique("prop"));
+        prop.setProperty(PROP_NAME, "propa");
+        prop.setProperty(PROP_PROPERTY_INDEX, true);
+        prop.setProperty(PROP_NOT_NULL_CHECK_ENABLED, true);
+        root.commit();
+
+        Tree test = root.getTree("/").addChild("test");
+        createNodeWithType(test, "a", "oak:TestNode").setProperty("propa", 
"a");
+        createNodeWithType(test, "b", "oak:TestNode").setProperty("propa", 
"c");
+        createNodeWithType(test, "c", "oak:TestNode").setProperty("propb", 
"e");
+        root.commit();
+
+        String query = "select [jcr:path] from [oak:TestNode] where [propa] is 
not null";
+        String explanation = explain(query);
+        assertThat(explanation, 
containsString(propertyExistenceQueryWithNullCheckExpectedExplain()));
+        assertEventually(() -> assertQuery(query, asList("/test/a", 
"/test/b")));
+    }
+
+    protected String propertyExistenceQueryWithNullCheckExpectedExplain() {
+        return indexOptions.getIndexType() + ":test1(/oak:index/test1) ";
+    }
+
+    @Test
+    public void propertyNonExistenceQuery() throws Exception {
+        NodeTypeRegistry.register(root, 
IOUtils.toInputStream(TestUtil.TEST_NODE_TYPE), "test nodeType");
+
+        Tree idx = indexOptions.setIndex(root, "test1",
+                
indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), 
TestUtil.NT_TEST, false, "propa", "propb"));
+        Tree props = TestUtil.newRulePropTree(idx, TestUtil.NT_TEST);
+        Tree prop = props.addChild(TestUtil.unique("prop"));
+        prop.setProperty(PROP_NAME, "propa");
+        prop.setProperty(PROP_PROPERTY_INDEX, true);
+        prop.setProperty(PROP_NULL_CHECK_ENABLED, true);
+        root.commit();
+
+        Tree test = root.getTree("/").addChild("test");
+        createNodeWithType(test, "a", "oak:TestNode").setProperty("propa", 
"a");
+        createNodeWithType(test, "b", "oak:TestNode").setProperty("propa", 
"c");
+        createNodeWithType(test, "c", "oak:TestNode").setProperty("propb", 
"e");
+        root.commit();
+
+        String query = "select [jcr:path] from [oak:TestNode] where [propa] is 
null";
+        String explanation = explain(query);
+        assertThat(explanation, 
containsString(propertyNonExistenceQueryExpectedExplain()));
+        assertEventually(() -> assertQuery(query, singletonList("/test/c")));
+    }
+
+    protected String propertyNonExistenceQueryExpectedExplain() {
+        return indexOptions.getIndexType() + ":test1(/oak:index/test1) ";
+    }
+
+    @Test
     public void dateQuery() throws Exception {
         Tree index = root.getTree("/");
         Tree indexDefn = createTestIndexNode(index, 
indexOptions.getIndexType());
@@ -202,8 +269,14 @@ public abstract class PropertyIndexCommonTest extends 
AbstractQueryTest {
                 Arrays.asList("/test/a", "/test/b", "/test/d")));
     }
 
-    private String explain(String query) {
+    protected String explain(String query) {
         String explain = "explain " + query;
         return executeQuery(explain, "JCR-SQL2").get(0);
     }
+
+    protected static Tree createNodeWithType(Tree t, String nodeName, String 
typeName){
+        t = t.addChild(nodeName);
+        t.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName, Type.NAME);
+        return t;
+    }
 }

Reply via email to