You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2012/03/20 17:15:20 UTC

svn commit: r1302967 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/query/ main/java/org/apache/jackrabbit/oak/query/ast/ main/java/org/apache/jackrabbit/oak/query/index/ test/java/org/apache/jackrabbit/oak/query/index/

Author: thomasm
Date: Tue Mar 20 16:15:20 2012
New Revision: 1302967

URL: http://svn.apache.org/viewvc?rev=1302967&view=rev
Log:
OAK-28 Query implementation

Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/LikePatternTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Value.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.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/Operator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java?rev=1302967&r1=1302966&r2=1302967&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java Tue Mar 20 16:15:20 2012
@@ -278,17 +278,17 @@ public class SQL2Parser {
     private ConstraintImpl parseCondition(DynamicOperandImpl left) throws ParseException {
         ConstraintImpl c;
         if (readIf("=")) {
-            c = factory.comparison(left, Operator.EQ, parseStaticOperand());
+            c = factory.comparison(left, Operator.EQUAL, parseStaticOperand());
         } else if (readIf("<>")) {
-            c = factory.comparison(left, Operator.NE, parseStaticOperand());
+            c = factory.comparison(left, Operator.NOT_EQUAL, parseStaticOperand());
         } else if (readIf("<")) {
-            c = factory.comparison(left, Operator.LT, parseStaticOperand());
+            c = factory.comparison(left, Operator.LESS_THAN, parseStaticOperand());
         } else if (readIf(">")) {
-            c = factory.comparison(left, Operator.GT, parseStaticOperand());
+            c = factory.comparison(left, Operator.GREATER_THAN, parseStaticOperand());
         } else if (readIf("<=")) {
-            c = factory.comparison(left, Operator.LE, parseStaticOperand());
+            c = factory.comparison(left, Operator.LESS_OR_EQUAL, parseStaticOperand());
         } else if (readIf(">=")) {
-            c = factory.comparison(left, Operator.GE, parseStaticOperand());
+            c = factory.comparison(left, Operator.GREATER_OR_EQUAL, parseStaticOperand());
         } else if (readIf("LIKE")) {
             c = factory.comparison(left, Operator.LIKE, parseStaticOperand());
         } else if (readIf("IS")) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Value.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Value.java?rev=1302967&r1=1302966&r2=1302967&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Value.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Value.java Tue Mar 20 16:15:20 2012
@@ -105,4 +105,9 @@ public class Value implements Comparable
         return value.toString().compareTo(o.toString());
     }
 
+    @Override
+    public String toString() {
+        return getString();
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java?rev=1302967&r1=1302966&r2=1302967&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java Tue Mar 20 16:15:20 2012
@@ -110,24 +110,25 @@ public abstract class AstVisitorBase imp
      * <li>Orderings</li>
      * <li>Columns</li>
      * </ul>
+     *
+     * @param query the query to visit
      */
-    public boolean visit(Query node) {
-        node.getSource().accept(this);
-        ConstraintImpl constraint = node.getConstraint();
+    public void visit(Query query) {
+        query.getSource().accept(this);
+        ConstraintImpl constraint = query.getConstraint();
         if (constraint != null) {
             constraint.accept(this);
         }
-        OrderingImpl[] orderings = node.getOrderings();
+        OrderingImpl[] orderings = query.getOrderings();
         if (orderings != null) {
             for (OrderingImpl ordering : orderings) {
                 ordering.accept(this);
             }
         }
-        ColumnImpl[] columns = node.getColumns();
+        ColumnImpl[] columns = query.getColumns();
         for (ColumnImpl column : columns) {
             column.accept(this);
         }
-        return true;
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java?rev=1302967&r1=1302966&r2=1302967&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java Tue Mar 20 16:15:20 2012
@@ -55,14 +55,14 @@ public class ComparisonImpl extends Cons
             return false;
         }
         switch (operator) {
-        case EQ:
+        case EQUAL:
             return v1.equals(v2);
-        case GE:
-        case GT:
-        case LE:
-        case LT:
+        case GREATER_OR_EQUAL:
+        case GREATER_THAN:
+        case LESS_OR_EQUAL:
+        case LESS_THAN:
             return operand1.currentValue() .equals(operand2.currentValue());
-        case NE:
+        case NOT_EQUAL:
             return !operand1.currentValue().equals(operand2.currentValue());
         case LIKE:
             return evaluateLike(v1, v2);
@@ -268,10 +268,10 @@ public class ComparisonImpl extends Cons
                 } else {
                     ValueFactory vf = query.getValueFactory();
                     if (lowerBound != null) {
-                        operand1.apply(f, Operator.GE, vf.createValue(lowerBound));
+                        operand1.apply(f, Operator.GREATER_OR_EQUAL, vf.createValue(lowerBound));
                     }
                     if (upperBound != null) {
-                        operand1.apply(f, Operator.LE, vf.createValue(upperBound));
+                        operand1.apply(f, Operator.LESS_OR_EQUAL, vf.createValue(upperBound));
                     }
                 }
             } else {

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=1302967&r1=1302966&r2=1302967&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 Tue Mar 20 16:15:20 2012
@@ -93,10 +93,10 @@ public class EquiJoinConditionImpl exten
         Value v1 = selector1.currentProperty(property1Name);
         Value v2 = selector2.currentProperty(property2Name);
         if (f.getSelector() == selector1 && v2 != null) {
-            f.restrictProperty(property1Name, Operator.EQ, v2);
+            f.restrictProperty(property1Name, Operator.EQUAL, v2);
         }
         if (f.getSelector() == selector2 && v1 != null) {
-            f.restrictProperty(property2Name, Operator.EQ, v1);
+            f.restrictProperty(property2Name, Operator.EQUAL, v1);
         }
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java?rev=1302967&r1=1302966&r2=1302967&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/Operator.java Tue Mar 20 16:15:20 2012
@@ -21,17 +21,17 @@ package org.apache.jackrabbit.oak.query.
  */
 public enum Operator {
 
-    EQ("="),
+    EQUAL("="),
 
-    NE("<>"),
+    NOT_EQUAL("<>"),
 
-    GT(">"),
+    GREATER_THAN(">"),
 
-    GE(">="),
+    GREATER_OR_EQUAL(">="),
 
-    LT("<"),
+    LESS_THAN("<"),
 
-    LE("<="),
+    LESS_OR_EQUAL("<="),
 
     LIKE("like");
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java?rev=1302967&r1=1302966&r2=1302967&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java Tue Mar 20 16:15:20 2012
@@ -67,7 +67,7 @@ public class PropertyExistenceImpl exten
     @Override
     public void apply(Filter f) {
         if (f.getSelector() == selector) {
-            f.restrictProperty(propertyName, Operator.NE, (Value) null);
+            f.restrictProperty(propertyName, Operator.NOT_EQUAL, (Value) null);
         }
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java?rev=1302967&r1=1302966&r2=1302967&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/Filter.java Tue Mar 20 16:15:20 2012
@@ -94,9 +94,9 @@ public class Filter {
      */
     private String valuePrefix;
 
-    private HashMap<String, PropertyValueCondition> propertyRanges = new HashMap<String, PropertyValueCondition>();
+    private HashMap<String, PropertyRestriction> propertyRestrictions = new HashMap<String, PropertyRestriction>();
 
-    static class PropertyValueCondition {
+    static class PropertyRestriction {
 
         /**
          * The name of the property.
@@ -106,22 +106,28 @@ public class Filter {
         /**
          * The first value to read, or null to read from the beginning.
          */
-        public String first;
+        public Value first;
 
         /**
-         * Whether only values bigger than the first value should be returned.
+         * Whether values that match the first should be returned.
          */
-        public boolean firstExcluding;
+        public boolean firstIncluding;
 
         /**
          * The last value to read, or null to read until the end.
          */
-        public String last;
+        public Value last;
 
         /**
-         * Whether only values smaller than the last value should be returned.
+         * Whether values that match the last should be returned.
          */
-        public boolean lastExcluding;
+        public boolean lastIncluding;
+
+        @Override
+        public String toString() {
+            return (first == null ? "" : ((firstIncluding ? "[" : "(") + first)) + ".." +
+                    (last == null ? "" : last + (lastIncluding ? "]" : ")"));
+        }
 
     }
 
@@ -138,6 +144,8 @@ public class Filter {
 
     /**
      * Get the path.
+     *
+     * @return the path
      */
     public String getPath() {
         return path;
@@ -176,7 +184,7 @@ public class Filter {
     }
 
     public void setAlwaysFalse() {
-        propertyRanges.clear();
+        propertyRestrictions.clear();
         valuePrefix = "none";
         nodeType = "none";
         path = "none";
@@ -192,8 +200,14 @@ public class Filter {
         return selector;
     }
 
-    public void restrictProperty(String propertyName, Operator op, Value value) {
-        restrictProperty(propertyName, op, value == null ? null : value.getString());
+    /**
+     * Get the restriction for the given property, if any.
+     *
+     * @param propertyName the property name
+     * @return the restriction or null
+     */
+    public PropertyRestriction getPropertyRestriction(String propertyName) {
+        return propertyRestrictions.get(propertyName);
     }
 
     public boolean testPath(String path) {
@@ -214,53 +228,62 @@ public class Filter {
         }
     }
 
-    public void restrictProperty(String propertyName, Operator op, String value) {
-        PropertyValueCondition x = propertyRanges.get(propertyName);
+    public void restrictProperty(String propertyName, Operator op, Value value) {
+        PropertyRestriction x = propertyRestrictions.get(propertyName);
         if (x == null) {
-            x = new PropertyValueCondition();
+            x = new PropertyRestriction();
             x.propertyName = propertyName;
-            propertyRanges.put(propertyName, x);
+            propertyRestrictions.put(propertyName, x);
         }
-        String oldFirst = x.first, oldLast = x.last;
+        Value oldFirst = x.first, oldLast = x.last;
         switch (op) {
-        case EQ:
+        case EQUAL:
             x.first = maxValue(oldFirst, value);
-            x.firstExcluding = false;
+            x.firstIncluding = x.first == oldFirst ? x.firstIncluding : true;
             x.last = minValue(oldLast, value);
-            x.lastExcluding = false;
+            x.lastIncluding = x.last == oldLast ? x.lastIncluding : true;
             break;
-        case NE:
-            // not null
+        case NOT_EQUAL:
+            if (value != null) {
+                throw new IllegalArgumentException("NOT_EQUAL only supported for NOT_EQUAL NULL");
+            }
             break;
-        case GT:
+        case GREATER_THAN:
             x.first = maxValue(oldFirst, value);
-            x.firstExcluding = true;
+            x.firstIncluding = false;
             break;
-        case GE:
+        case GREATER_OR_EQUAL:
             x.first = maxValue(oldFirst, value);
-            x.firstExcluding = x.first == oldFirst ? x.firstExcluding : false;
+            x.firstIncluding = x.first == oldFirst ? x.firstIncluding : true;
             break;
-        case LT:
+        case LESS_THAN:
             x.last = minValue(oldLast, value);
-            x.lastExcluding = true;
+            x.lastIncluding = false;
             break;
-        case LE:
+        case LESS_OR_EQUAL:
             x.last = minValue(oldLast, value);
-            x.lastExcluding = x.last == oldLast ? x.lastExcluding : false;
+            x.lastIncluding = x.last == oldLast ? x.lastIncluding : true;
             break;
         case LIKE:
-            throw new RuntimeException("LIKE is not supported");
+            throw new IllegalArgumentException("LIKE is not supported");
+        }
+        if (x.first != null && x.last != null) {
+            if (x.first.compareTo(x.last) > 0) {
+                setAlwaysFalse();
+            } else if (x.first.compareTo(x.last) == 0 && (!x.firstIncluding || !x.lastIncluding)) {
+                setAlwaysFalse();
+            }
         }
     }
 
-    static String maxValue(String a, String b) {
+    static Value maxValue(Value a, Value b) {
         if (a == null) {
             return b;
         }
         return a.compareTo(b) < 0 ? b : a;
     }
 
-    static String minValue(String a, String b) {
+    static Value minValue(Value a, Value b) {
         if (a == null) {
             return b;
         }

Added: 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=1302967&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java Tue Mar 20 16:15:20 2012
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.query.index;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import java.util.ArrayList;
+import java.util.Random;
+import org.apache.jackrabbit.oak.query.Value;
+import org.apache.jackrabbit.oak.query.ValueFactory;
+import org.apache.jackrabbit.oak.query.ast.Operator;
+import org.apache.jackrabbit.oak.query.index.Filter.PathRestriction;
+import org.junit.Test;
+
+/**
+ * Tests the Filter class.
+ */
+public class FilterTest {
+
+    @Test
+    public void propertyRestriction() {
+        Value one = new ValueFactory().createValue("1");
+        Value two = new ValueFactory().createValue("2");
+
+        Filter f = new Filter(null);
+        assertTrue(null == f.getPropertyRestriction("x"));
+        f.restrictProperty("x", Operator.LESS_OR_EQUAL, two);
+        assertEquals("..2]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.GREATER_OR_EQUAL, one);
+        assertEquals("[1..2]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.GREATER_THAN, one);
+        assertEquals("(1..2]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.LESS_THAN, two);
+        assertEquals("(1..2)", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.EQUAL, two);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictProperty("x", Operator.EQUAL, one);
+        assertEquals("[1..1]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.EQUAL, one);
+        assertEquals("[1..1]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.GREATER_OR_EQUAL, one);
+        assertEquals("[1..1]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.LESS_OR_EQUAL, one);
+        assertEquals("[1..1]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.GREATER_THAN, one);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictProperty("x", Operator.EQUAL, one);
+        assertEquals("[1..1]", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.LESS_THAN, one);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictProperty("x", Operator.NOT_EQUAL, null);
+        assertEquals("..", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.LESS_THAN, one);
+        assertEquals("..1)", f.getPropertyRestriction("x").toString());
+        f.restrictProperty("x", Operator.EQUAL, two);
+        assertTrue(f.isAlwaysFalse());
+
+    }
+
+    @Test
+    public void pathRestrictionsRandomized() throws Exception {
+        ArrayList<String> paths = new ArrayList<String>();
+        // create paths /a, /b, /c, /a/a, /a/b, ... /c/c/c
+        paths.add("/");
+        for (int i = 'a'; i <= 'c'; i++) {
+            String p1 = "/" + (char) i;
+            paths.add(p1);
+            for (int j = 'a'; j <= 'c'; j++) {
+                String p2 = "/" + (char) j;
+                paths.add(p1 + p2);
+                for (int k = 'a'; k <= 'c'; k++) {
+                    String p3 = "/" + (char) k;
+                    paths.add(p1 + p2 + p3);
+                }
+            }
+        }
+        Random r = new Random(1);
+        for (int i = 0; i < 10000; i++) {
+            String p1 = paths.get(r.nextInt(paths.size()));
+            String p2 = paths.get(r.nextInt(paths.size()));
+            PathRestriction r1 = PathRestriction.values()[r.nextInt(PathRestriction.values().length)];
+            PathRestriction r2 = PathRestriction.values()[r.nextInt(PathRestriction.values().length)];
+            Filter f1 = new Filter(null);
+            f1.restrictPath(p1, r1);
+            Filter f2 = new Filter(null);
+            f2.restrictPath(p2, r2);
+            Filter fc = new Filter(null);
+            fc.restrictPath(p1, r1);
+            fc.restrictPath(p2, r2);
+            int tooMany = 0;
+            for (String p : paths) {
+                boolean expected = f1.testPath(p) && f2.testPath(p);
+                boolean got = fc.testPath(p);
+                if (expected == got) {
+                    // good
+                } else if (expected && !got) {
+                    fc = new Filter(null);
+                    fc.restrictPath(p1, r1);
+                    fc.restrictPath(p2, r2);
+                    fail("not matched: " + p1 + "/" + r1.name() + " && " + p2 + "/" + r2.name());
+                } else {
+                    // not great, but not a problem
+                    tooMany++;
+                }
+            }
+            if (tooMany > 3) {
+                fail("too many matches: " + p1 + "/" + r1.name() + " && " + p2 + "/" + r2.name() + " superfluous: " + tooMany);
+            }
+        }
+    }
+
+    @Test
+    public void pathRestrictions() throws Exception {
+        Filter f = new Filter(null);
+        assertEquals("/", f.getPath());
+        assertEquals(PathRestriction.ALL_CHILDREN, f.getPathRestriction());
+
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        f.restrictPath("/test2", PathRestriction.ALL_CHILDREN);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        assertEquals("/test", f.getPath());
+        assertEquals(PathRestriction.ALL_CHILDREN, f.getPathRestriction());
+        f.restrictPath("/test/x", PathRestriction.DIRECT_CHILDREN);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        f.restrictPath("/test/x/y", PathRestriction.PARENT);
+        assertEquals("/test/x/y", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+
+        f = new Filter(null);
+        f.restrictPath("/test", PathRestriction.DIRECT_CHILDREN);
+        f.restrictPath("/test/x/y", PathRestriction.PARENT);
+        assertEquals("/test/x/y", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test/y", PathRestriction.DIRECT_CHILDREN);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test/x/y", PathRestriction.PARENT);
+        f.restrictPath("/test/x", PathRestriction.EXACT);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.EXACT, f.getPathRestriction());
+        f.restrictPath("/test/y", PathRestriction.EXACT);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        f.restrictPath("/test", PathRestriction.PARENT);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test/x", PathRestriction.PARENT);
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test/x", PathRestriction.ALL_CHILDREN);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        f.restrictPath("/test", PathRestriction.EXACT);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test", PathRestriction.DIRECT_CHILDREN);
+        f.restrictPath("/test/x", PathRestriction.EXACT);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.EXACT, f.getPathRestriction());
+
+        f = new Filter(null);
+        f.restrictPath("/test", PathRestriction.DIRECT_CHILDREN);
+        f.restrictPath("/test/x/y", PathRestriction.EXACT);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test/x", PathRestriction.PARENT);
+        f.restrictPath("/", PathRestriction.ALL_CHILDREN);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test/y", PathRestriction.EXACT);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test", PathRestriction.DIRECT_CHILDREN);
+        assertEquals("/test", f.getPath());
+        assertEquals(PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        f.restrictPath("/", PathRestriction.ALL_CHILDREN);
+        assertEquals("/test", f.getPath());
+        assertEquals(PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        assertEquals("/test", f.getPath());
+        assertEquals(PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        f.restrictPath("/test/x/y", PathRestriction.PARENT);
+        assertEquals("/test/x/y", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test2", PathRestriction.ALL_CHILDREN);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test/x", PathRestriction.EXACT);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.EXACT, f.getPathRestriction());
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        f.restrictPath("/test", PathRestriction.DIRECT_CHILDREN);
+        f.restrictPath("/test/x/y", PathRestriction.PARENT);
+        f.restrictPath("/test/y", PathRestriction.DIRECT_CHILDREN);
+        assertTrue(f.isAlwaysFalse());
+
+        f = new Filter(null);
+        f.restrictPath("/test/x/y", PathRestriction.PARENT);
+        assertEquals("/test/x/y", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test/x", PathRestriction.PARENT);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test", PathRestriction.ALL_CHILDREN);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test", PathRestriction.DIRECT_CHILDREN);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test/x", PathRestriction.PARENT);
+        assertEquals("/test/x", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test", PathRestriction.PARENT);
+        assertEquals("/test", f.getPath());
+        assertEquals(PathRestriction.PARENT, f.getPathRestriction());
+        f.restrictPath("/test2", PathRestriction.EXACT);
+        assertTrue(f.isAlwaysFalse());
+
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/LikePatternTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/LikePatternTest.java?rev=1302967&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/LikePatternTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/LikePatternTest.java Tue Mar 20 16:15:20 2012
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.query.index;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import org.apache.jackrabbit.oak.query.ast.ComparisonImpl;
+import org.junit.Test;
+
+public class LikePatternTest {
+
+    @Test
+    public void pattern() {
+        pattern("%_", "X", "", null, null);
+        pattern("A%", "A", "X", "A", "B");
+        pattern("A%%", "A", "X", "A", "B");
+        pattern("%\\_%", "A_A", "AAA", null, null);
+    }
+
+    private void pattern(String pattern, String match, String noMatch, String lower, String upper) {
+        ComparisonImpl.LikePattern p = new ComparisonImpl.LikePattern(pattern);
+        if (match != null) {
+            assertTrue(p.matches(match));
+        }
+        if (noMatch != null) {
+            assertFalse(p.matches(noMatch));
+        }
+        assertEquals(lower, p.getLowerBound());
+        assertEquals(upper, p.getUpperBound());
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java?rev=1302967&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/TraversingCursorTest.java Tue Mar 20 16:15:20 2012
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.query.index;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.apache.jackrabbit.mk.MicroKernelFactory;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests the TraversingCursor.
+ */
+public class TraversingCursorTest {
+
+    MicroKernel mk;
+    String head;
+
+    @Before
+    public void setUp() {
+        mk = MicroKernelFactory.getInstance("simple:/target/temp;clear");
+        head = mk.getHeadRevision();
+    }
+
+    @After
+    public void tearDown() {
+        mk.dispose();
+    }
+
+    @Test
+    public void traverse() throws Exception {
+        TraversingReader r = new TraversingReader(mk);
+        traverse(r);
+    }
+
+    @Test
+    public void traverseBlockwise() throws Exception {
+        TraversingReader r = new TraversingReader(mk);
+        r.setChildBlockSize(2);
+        traverse(r);
+    }
+
+    private void traverse(TraversingReader r) {
+        head = mk.commit("/", "+ \"parents\": { \"p0\": {\"id\": \"0\"}, \"p1\": {\"id\": \"1\"}, \"p2\": {\"id\": \"2\"}}", head, "");
+        head = mk.commit("/", "+ \"children\": { \"c1\": {\"p\": \"1\"}, \"c2\": {\"p\": \"1\"}, \"c3\": {\"p\": \"2\"}, \"c4\": {\"p\": \"3\"}}", head, "");
+        Filter f = new Filter(null);
+        Cursor c;
+        f.setPath("/");
+        c = r.query(f, head);
+        String[] list = {"/", "/parents", "/parents/p0", "/parents/p1",  "/parents/p2",
+                "/children", "/children/c1", "/children/c2", "/children/c3", "/children/c4"};
+        for (String s : list) {
+            assertTrue(c.next());
+            assertEquals(s, c.currentPath());
+        }
+        assertFalse(c.next());
+        assertFalse(c.next());
+        f.setPath("/nowhere");
+        c = r.query(f, head);
+        assertFalse(c.next());
+        assertFalse(c.next());
+    }
+
+}