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


Reply via email to