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;
+ }
}