Author: thomasm
Date: Wed Feb 18 13:22:13 2015
New Revision: 1660627
URL: http://svn.apache.org/r1660627
Log:
OAK-2530 Support IS NULL based property restrictions in the query engine
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java
Wed Feb 18 13:22:13 2015
@@ -131,7 +131,7 @@ public class OrderedPropertyIndex implem
String operation = null;
PropertyValue value = null;
// TODO support pr.list
- if (pr.first == null && pr.last == null) {
+ if (pr.isNotNullRestriction()) {
// open query: [property] is not null
operation = "is not null";
} else if (pr.first != null && pr.first.equals(pr.last) &&
pr.firstIncluding
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java
Wed Feb 18 13:22:13 2015
@@ -268,7 +268,7 @@ public class OrderedPropertyIndexLookup
if (definition != null) {
PropertyValue value = null;
boolean createPlan = false;
- if (pr.first == null && pr.last == null) {
+ if (pr.isNotNullRestriction()) {
// open query: [property] is not null
value = null;
createPlan = true;
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
Wed Feb 18 13:22:13 2015
@@ -121,7 +121,7 @@ class PropertyIndex implements QueryInde
return values;
}
- private PropertyIndexPlan plan(NodeState root, Filter filter) {
+ private static PropertyIndexPlan plan(NodeState root, Filter filter) {
PropertyIndexPlan bestPlan = null;
// TODO support indexes on a path
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
Wed Feb 18 13:22:13 2015
@@ -138,6 +138,10 @@ public class PropertyIndexPlan {
}
if (restriction != null) {
+ if (restriction.isNullRestriction()) {
+ // covering indexes are not currently supported
+ continue;
+ }
Set<String> values = getValues(restriction);
double cost = strategy.count(filter, root, definition,
values, MAX_COST);
if (cost < bestCost) {
@@ -227,7 +231,7 @@ public class PropertyIndexPlan {
}
return values;
} else {
- // processed as "[property] is not null"
+ // "[property] is not null" or "[property] is null"
return null;
}
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
Wed Feb 18 13:22:13 2015
@@ -106,9 +106,14 @@ public class EquiJoinConditionImpl exten
p2 = null;
}
}
- // always set the condition, even if unkown ( -> is not null)
String p1n = normalizePropertyName(property1Name);
- f.restrictProperty(p1n, Operator.EQUAL, p2);
+ if (p2 == null) {
+ // always set the condition,
+ // even if unknown (in which case it is converted to "is not
null")
+ f.restrictProperty(p1n, Operator.NOT_EQUAL, null);
+ } else {
+ f.restrictProperty(p1n, Operator.EQUAL, p2);
+ }
}
if (f.getSelector().equals(selector2)) {
PropertyValue p1 = selector1.currentProperty(property1Name);
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyInexistenceImpl.java
Wed Feb 18 13:22:13 2015
@@ -102,8 +102,6 @@ public class PropertyInexistenceImpl ext
@Override
public void restrict(FilterImpl f) {
- // we don't support covering indexes,
- // so there is no optimization anyway, and
// we need to be careful with "property IS NULL"
// because this might cause an index
// to ignore the join condition "property = x"
@@ -113,6 +111,13 @@ public class PropertyInexistenceImpl ext
// must not result in the index to check for
// "b.y is null", because that would alter the
// result
+ if (selector.isOuterJoinRightHandSide()) {
+ return;
+ }
+ if (f.getSelector().equals(selector)) {
+ String pn = normalizePropertyName(propertyName);
+ f.restrictProperty(pn, Operator.EQUAL, null);
+ }
}
@Override
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
Wed Feb 18 13:22:13 2015
@@ -225,6 +225,14 @@ public interface Filter {
* If not restricted, this field is set to PropertyType.UNDEFINED.
*/
public int propertyType = PropertyType.UNDEFINED;
+
+ public boolean isNullRestriction() {
+ return first == null && last == null && lastIncluding &&
firstIncluding;
+ }
+
+ public boolean isNotNullRestriction() {
+ return first == null && last == null && !lastIncluding &&
!firstIncluding;
+ }
@Override
public String toString() {
@@ -248,6 +256,11 @@ public interface Filter {
}
private String toStringFromTo() {
+ if (isNullRestriction()) {
+ return "is null";
+ } else if (isNotNullRestriction()) {
+ return "is not null";
+ }
String f = first == null ? "" : first.toString();
String l = last == null ? "" : last.toString();
if (f.equals(l)) {
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
Wed Feb 18 13:22:13 2015
@@ -21,6 +21,7 @@ import static junit.framework.Assert.ass
import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
import static
org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
import static
org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
+import static org.junit.Assert.assertEquals;
import java.text.ParseException;
@@ -52,5 +53,19 @@ public class FilterTest {
Filter f = createFilter("//*[(@prop = 'aaa' and @prop = 'bbb' and
@prop = 'ccc')]");
assertFalse(f.isAlwaysFalse());
}
+
+ @Test
+ public void isNull() throws Exception {
+ // this can refer to a multi-valued property
+ Filter f = createFilter("//*[not(@c)]");
+ assertEquals("[is null]", f.getPropertyRestrictions("c").toString());
+ }
+
+ @Test
+ public void isNotNull() throws Exception {
+ // this can refer to a multi-valued property
+ Filter f = createFilter("//*[@c]");
+ assertEquals("[is not null]",
f.getPropertyRestrictions("c").toString());
+ }
}
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java
Wed Feb 18 13:22:13 2015
@@ -123,18 +123,18 @@ public class FilterTest {
f = new FilterImpl();
f.restrictProperty("x", Operator.NOT_EQUAL, null);
assertEquals(
- "Filter(, path=*, property=[x=[]])",
+ "Filter(, path=*, property=[x=[is not null]])",
f.toString());
f.restrictProperty("x", Operator.LESS_THAN, one);
assertEquals(
- "Filter(, path=*, property=[x=[, ..1)]])",
+ "Filter(, path=*, property=[x=[is not null, ..1)]])",
f.toString());
// this should replace the range with an equality
// (which is faster, and correct even when using multi-valued
properties)
f.restrictProperty("x", Operator.EQUAL, two);
assertEquals(
- "Filter(, path=*, property=[x=[, ..1), 2]])",
+ "Filter(, path=*, property=[x=[is not null, ..1), 2]])",
f.toString());
}
Modified:
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt
Wed Feb 18 13:22:13 2015
@@ -29,7 +29,7 @@
commit / + "test": { "a": { "name": "Hello" }, "b": { "name" : "World" }}
-select * from [nt:base]
+select * from [nt:base]
where [a] = 1 and [b] = 2 and [b] = 3 or [c] = 4
select [jcr:path]
Modified:
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
Wed Feb 18 13:22:13 2015
@@ -24,6 +24,21 @@
# * new tests are typically be added on top, after the syntax docs
# * use ascii character only
+explain select * from [nt:base] as a
+ left outer join [nt:base] as b on a.x=b.y
+ where a.y is null and b.z = 1
+[nt:base] as [a] /* traverse "*"
+ where [a].[y] is null */ left outer join [nt:base] as [b] /* traverse "*"
+ where [b].[z] = 1 */
+ on [a].[x] = [b].[y]
+
+explain select * from [nt:base] as a
+ right outer join [nt:base] as b on a.x=b.y
+ where a.y is null and b.z = 1
+[nt:base] as [b] /* traverse "*"
+ where [b].[z] = 1 */ left outer join [nt:base] as [a] /* traverse "*" */
+ on [a].[x] = [b].[y]
+
explain select * from [nt:base] where (p=1 or p=2) and (p=3 or p=4)
[nt:base] as [nt:base] /* traverse "*"
where ([nt:base].[p] in(1, 2))
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
Wed Feb 18 13:22:13 2015
@@ -139,6 +139,10 @@ class IndexPlanner {
//for property index
if (indexingRule.propertyIndexEnabled) {
for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
+ if (pr.isNullRestriction()) {
+ // ignore for planning
+ continue;
+ }
PropertyDefinition pd =
indexingRule.getConfig(pr.propertyName);
if (pd != null && pd.propertyIndexEnabled()) {
indexedProps.add(pr.propertyName);
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
Wed Feb 18 13:22:13 2015
@@ -604,8 +604,8 @@ public class LuceneIndex implements Adva
IndexingRule rule =
indexDefinition.getApplicableIndexingRule(JcrConstants.NT_BASE);
for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
- if (pr.first == null && pr.last == null) {
- // ignore property existence checks, Lucene can't to 'property
+ if (pr.isNullRestriction() || pr.isNotNullRestriction()) {
+ // ignore property existence checks, Lucene can't do 'property
// is not null' queries (OAK-1208)
continue;
}
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java?rev=1660627&r1=1660626&r2=1660627&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
Wed Feb 18 13:22:13 2015
@@ -742,7 +742,7 @@ public class LucenePropertyIndex impleme
in.add(NumericRangeQuery.newLongRange(pr.propertyName,
dateVal, dateVal, true, true), BooleanClause.Occur.SHOULD);
}
return in;
- } else if (pr.first == null && pr.last == null ) {
+ } else if (pr.isNotNullRestriction()) {
// not null. For date lower bound of zero can be used
return NumericRangeQuery.newLongRange(pr.propertyName, 0L,
Long.MAX_VALUE, true, true);
}