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 ju...@apache.org on 2014/07/15 17:17:01 UTC

svn commit: r1610723 - 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/fulltext/ test/java/org/apache/jackrabbit/oak/query/ast/ t...

Author: jukka
Date: Tue Jul 15 15:17:01 2014
New Revision: 1610723

URL: http://svn.apache.org/r1610723
Log:
OAK-1965: Support for constraints like: foo = 'X' OR bar = 'Y'

Extend OrImpl and AndImpl to support more than two component constraints,
and use that mechanism for improved simplification.

Replace the getInMap() feature with more specific code in OrImpl, as that's
the only place where the functionality is needed.

Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ast/SimplifyTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.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/ChildNodeImpl.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/ConstraintImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/InImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.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/ast/PropertyInexistenceImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextAnd.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextOr.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/SimpleExcerptProvider.java
    jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java Tue Jul 15 15:17:01 2014
@@ -154,6 +154,13 @@ public class QueryImpl implements Query 
 
         final QueryImpl query = this;
 
+        if (constraint != null) {
+            // need to do this *before* the visitation below, as the
+            // simplify() method does not always keep the query reference
+            // passed in setQuery(). TODO: avoid that mutability concern
+            constraint = constraint.simplify();
+        }
+
         new AstVisitorBase() {
 
             @Override
@@ -347,9 +354,7 @@ public class QueryImpl implements Query 
             }
 
         }.visit(this);
-        if (constraint != null) {
-            constraint = constraint.simplify();
-        }
+
         source.setQueryConstraint(constraint);
         for (ColumnImpl column : columns) {
             column.bindSelector(source);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java Tue Jul 15 15:17:01 2014
@@ -18,115 +18,116 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.Map.Entry;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Sets.newHashSet;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
 /**
  * An AND condition.
  */
 public class AndImpl extends ConstraintImpl {
 
-    private ConstraintImpl constraint1, constraint2;
+    private final List<ConstraintImpl> constraints;
 
-    public AndImpl(ConstraintImpl constraint1, ConstraintImpl constraint2) {
-        this.constraint1 = constraint1;
-        this.constraint2 = constraint2;
+    AndImpl(List<ConstraintImpl> constraints) {
+        checkArgument(constraints.size() > 1);
+        this.constraints = constraints;
     }
 
-    public ConstraintImpl getConstraint1() {
-        return constraint1;
+    public AndImpl(ConstraintImpl constraint1, ConstraintImpl constraint2) {
+        this(Arrays.asList(constraint1, constraint2));
     }
 
-    public ConstraintImpl getConstraint2() {
-        return constraint2;
+    public List<ConstraintImpl> getConstraints() {
+        return constraints;
     }
 
     @Override
     public ConstraintImpl simplify() {
-        constraint1 = constraint1.simplify();
-        constraint2 = constraint2.simplify();
-        if (constraint1.equals(constraint2)) {
-            return constraint1;
+        // Use LinkedHashSet to eliminate duplicate constraints while keeping
+        // the ordering for test cases (and clients?) that depend on it
+        LinkedHashSet<ConstraintImpl> simplified = newLinkedHashSet();
+        boolean changed = false; // keep track of changes in simplification
+
+        for (ConstraintImpl constraint : constraints) {
+            ConstraintImpl simple = constraint.simplify();
+            if (simple instanceof AndImpl) {
+                // unwind nested AND constraints
+                simplified.addAll(((AndImpl) simple).constraints);
+                changed = true;
+            } else if (simplified.add(simple)) {
+                // check if this constraint got simplified
+                changed = changed || simple != constraint;
+            } else {
+                // this constraint was a duplicate of a previous one
+                changed = true;
+            }
+        }
+
+        if (simplified.size() == 1) {
+            return simplified.iterator().next();
+        } else if (changed) {
+            return new AndImpl(newArrayList(simplified));
+        } else {
+            return this;
         }
-        return this;
     }
-    
+
     @Override
     public Set<PropertyExistenceImpl> getPropertyExistenceConditions() {
-        Set<PropertyExistenceImpl> s1 = constraint1.getPropertyExistenceConditions();
-        Set<PropertyExistenceImpl> s2 = constraint2.getPropertyExistenceConditions();
-        Set<PropertyExistenceImpl> result = Sets.newHashSet(s1);
-        result.addAll(s2);
+        Set<PropertyExistenceImpl> result = newHashSet();
+        for (ConstraintImpl constraint : constraints) {
+            result.addAll(constraint.getPropertyExistenceConditions());
+        }
         return result;
     }
     
     @Override
     public FullTextExpression getFullTextConstraint(SelectorImpl s) {
-        FullTextExpression f1 = constraint1.getFullTextConstraint(s);
-        FullTextExpression f2 = constraint2.getFullTextConstraint(s);
-        if (f1 == null) {
-            return f2;
-        } else if (f2 == null) {
-            return f1;
-        }
-        ArrayList<FullTextExpression> list = new ArrayList<FullTextExpression>();
-        list.add(f1);
-        list.add(f2);
-        return new FullTextAnd(list);
+        List<FullTextExpression> list = newArrayList();
+        for (ConstraintImpl constraint : constraints) {
+            FullTextExpression expression = constraint.getFullTextConstraint(s);
+            if (expression != null) {
+                list.add(expression);
+            }
+        }
+        switch (list.size()) {
+        case 0:
+            return null;
+        case 1:
+            return list.iterator().next();
+        default:
+            return new FullTextAnd(list);
+        }
     }
     
     @Override
     public Set<SelectorImpl> getSelectors() {
-        Set<SelectorImpl> s1 = constraint1.getSelectors();
-        Set<SelectorImpl> s2 = constraint1.getSelectors();
-        if (s1.isEmpty()) {
-            return s2;
-        } else if (s2.isEmpty()) {
-            return s1;
-        }
-        return Sets.union(s1, s2);
-    }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        Map<DynamicOperandImpl, Set<StaticOperandImpl>> m1 = constraint1.getInMap();
-        Map<DynamicOperandImpl, Set<StaticOperandImpl>> m2 = constraint2.getInMap();
-        if (m1.isEmpty()) {
-            return m2;
-        } else if (m2.isEmpty()) {
-            return m1;
-        }
-        Map<DynamicOperandImpl, Set<StaticOperandImpl>> result = Maps.newHashMap();
-        result.putAll(m1);
-        for (Entry<DynamicOperandImpl, Set<StaticOperandImpl>> e2 : m2.entrySet()) {
-            Set<StaticOperandImpl> s = result.get(e2.getKey());
-            if (s != null) {
-                // OAK-1933
-                // a property can have multiple values at the same time,
-                // so that "where a=1 and a=2" needs to be kept and can not
-                // be reduced to "where false" - in fact, we could 
-                // extend it to "where a in (1, 2)" so that an index can be used,
-                // but we might as well keep it at "where a = 1" as that would
-                // also use an index
-            } else {
-                result.put(e2.getKey(), e2.getValue());
-            }
+        Set<SelectorImpl> result = newHashSet();
+        for (ConstraintImpl constraint : constraints) {
+            result.addAll(constraint.getSelectors());
         }
         return result;
     }
 
     @Override
     public boolean evaluate() {
-        return constraint1.evaluate() && constraint2.evaluate();
+        for (ConstraintImpl constraint : constraints) {
+            if (!constraint.evaluate()) {
+                return false;
+            }
+        }
+        return true;
     }
 
     @Override
@@ -135,21 +136,47 @@ public class AndImpl extends ConstraintI
     }
 
     @Override
+    public void restrict(FilterImpl f) {
+        for (ConstraintImpl constraint : constraints) {
+            constraint.restrict(f);
+        }
+    }
+
+    @Override
+    public void restrictPushDown(SelectorImpl s) {
+        for (ConstraintImpl constraint : constraints) {
+            constraint.restrictPushDown(s);
+        }
+    }
+
+    //------------------------------------------------------------< Object >--
+
+    @Override
     public String toString() {
-        return protect(constraint1) + " and " + protect(constraint2);
+        StringBuilder builder = new StringBuilder();
+        for (ConstraintImpl constraint : constraints) {
+            if (builder.length() > 0) {
+                builder.append(" and ");
+            }
+            builder.append(protect(constraint));
+        }
+        return builder.toString();
     }
 
     @Override
-    public void restrict(FilterImpl f) {
-        constraint1.restrict(f);
-        constraint2.restrict(f);
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        } else if (that instanceof AndImpl) {
+            return constraints.equals(((AndImpl) that).constraints);
+        } else {
+            return false;
+        }
     }
 
     @Override
-    public void restrictPushDown(SelectorImpl s) {
-        constraint1.restrictPushDown(s);
-        constraint2.restrictPushDown(s);
+    public int hashCode() {
+        return constraints.hashCode();
     }
 
 }
-

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=1610723&r1=1610722&r2=1610723&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 Jul 15 15:17:01 2014
@@ -28,8 +28,9 @@ public abstract class AstVisitorBase imp
      */
     @Override
     public boolean visit(AndImpl node) {
-        node.getConstraint1().accept(this);
-        node.getConstraint2().accept(this);
+        for (ConstraintImpl constraint : node.getConstraints()) {
+            constraint.accept(this);
+        }
         return true;
     }
 
@@ -130,8 +131,9 @@ public abstract class AstVisitorBase imp
      */
     @Override
     public boolean visit(OrImpl node) {
-        node.getConstraint1().accept(this);
-        node.getConstraint2().accept(this);
+        for (ConstraintImpl constraint : node.getConstraints()) {
+            constraint.accept(this);
+        }
         return true;
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeImpl.java Tue Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.commons.PathUtils;
@@ -64,11 +63,6 @@ public class ChildNodeImpl extends Const
         return Collections.singleton(selector);
     }
 
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }    
-    
     @Override
     public boolean evaluate() {
         String p = selector.currentPath();

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=1610723&r1=1610722&r2=1610723&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 Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.PropertyType;
@@ -73,14 +72,6 @@ public class ComparisonImpl extends Cons
         return operand1.getSelectors();
     }
     
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        if (operator == Operator.EQUAL) {
-            return Collections.singletonMap(operand1, Collections.singleton(operand2));
-        }
-        return Collections.emptyMap();
-    }    
-    
     @Override
     public boolean evaluate() {
         // JCR 2.0 spec, 6.7.16 Comparison:

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ConstraintImpl.java Tue Jul 15 15:17:01 2014
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
@@ -76,13 +75,6 @@ public abstract class ConstraintImpl ext
      * @return the set of selectors (possibly empty)
      */
     public abstract Set<SelectorImpl> getSelectors();
-    
-    /**
-     * Get the map of "in(..)" conditions.
-     * 
-     * @return the map
-     */
-    public abstract Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap();
 
     /**
      * Apply the condition to the filter, further restricting the filter if

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DescendantNodeImpl.java Tue Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.commons.PathUtils;
@@ -49,11 +48,6 @@ public class DescendantNodeImpl extends 
     public Set<SelectorImpl> getSelectors() {
         return Collections.singleton(selector);
     }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }    
 
     @Override
     public boolean evaluate() {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java Tue Jul 15 15:17:01 2014
@@ -23,7 +23,6 @@ import static org.apache.jackrabbit.oak.
 
 import java.text.ParseException;
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.PropertyState;
@@ -149,11 +148,6 @@ public class FullTextSearchImpl extends 
     public Set<SelectorImpl> getSelectors() {
         return Collections.singleton(selector);
     }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }    
 
     @Override
     public boolean evaluate() {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/InImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/InImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/InImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/InImpl.java Tue Jul 15 15:17:01 2014
@@ -24,7 +24,6 @@ import static com.google.common.collect.
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.PropertyType;
@@ -85,12 +84,6 @@ public class InImpl extends ConstraintIm
     public Set<SelectorImpl> getSelectors() {
         return operand1.getSelectors();
     }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        Set<StaticOperandImpl> operand2set = newHashSet(operand2);
-        return Collections.singletonMap(operand1, operand2set);
-    }  
 
     @Override
     public boolean evaluate() {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java Tue Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
@@ -75,11 +74,6 @@ public class NativeFunctionImpl extends 
     }
 
     @Override
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }
-
-    @Override
     public void restrict(FilterImpl f) {
         if (f.getSelector().equals(selector)) {
             PropertyValue v = nativeSearchExpression.currentValue();

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NotImpl.java Tue Jul 15 15:17:01 2014
@@ -18,8 +18,10 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
+import static com.google.common.collect.Lists.newArrayList;
+
 import java.util.Collections;
-import java.util.Map;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
@@ -29,7 +31,7 @@ import org.apache.jackrabbit.oak.query.i
  */
 public class NotImpl extends ConstraintImpl {
 
-    private final ConstraintImpl constraint;
+    private ConstraintImpl constraint;
 
     public NotImpl(ConstraintImpl constraint) {
         this.constraint = constraint;
@@ -39,6 +41,38 @@ public class NotImpl extends ConstraintI
         return constraint;
     }
 
+    /**
+     * Apply DeMorgan's Laws to push AND/OR constraints higher.
+     */
+    @Override
+    public ConstraintImpl simplify() {
+        ConstraintImpl simple = constraint.simplify();
+        if (simple instanceof AndImpl) {
+            // not (X and Y) == (not X) or (not Y)
+            AndImpl and = (AndImpl) simple;
+            List<ConstraintImpl> constraints = newArrayList();
+            for (ConstraintImpl constraint : and.getConstraints()) {
+                constraints.add(new NotImpl(constraint));
+            }
+            return new OrImpl(constraints).simplify();
+        } else if (simple instanceof OrImpl) {
+            // not (X or Y) == (not X) and (not Y)
+            OrImpl or = (OrImpl) simple;
+            List<ConstraintImpl> constraints = newArrayList();
+            for (ConstraintImpl constraint : or.getConstraints()) {
+                constraints.add(new NotImpl(constraint));
+            }
+            return new AndImpl(constraints).simplify();
+        } else if (simple instanceof NotImpl) {
+            // not not X == X
+            return ((NotImpl) simple).constraint;
+        } else if (simple != constraint) {
+            return new NotImpl(simple);
+        } else {
+            return this;
+        }
+    }
+
     @Override
     public boolean evaluate() {
         return !constraint.evaluate();
@@ -53,11 +87,6 @@ public class NotImpl extends ConstraintI
     public Set<SelectorImpl> getSelectors() {
         return constraint.getSelectors();
     }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }    
 
     @Override
     boolean accept(AstVisitor v) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/OrImpl.java Tue Jul 15 15:17:01 2014
@@ -18,8 +18,18 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import java.util.ArrayList;
-import java.util.Map;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newLinkedHashMap;
+import static com.google.common.collect.Sets.newHashSet;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+import static org.apache.jackrabbit.oak.query.ast.Operator.EQUAL;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
 
@@ -27,119 +37,154 @@ import org.apache.jackrabbit.oak.query.f
 import org.apache.jackrabbit.oak.query.fulltext.FullTextOr;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
 /**
  * An "or" condition.
  */
 public class OrImpl extends ConstraintImpl {
 
-    private ConstraintImpl constraint1, constraint2;
+    private final List<ConstraintImpl> constraints;
 
-    public OrImpl(ConstraintImpl constraint1, ConstraintImpl constraint2) {
-        this.constraint1 = constraint1;
-        this.constraint2 = constraint2;
+    OrImpl(List<ConstraintImpl> constraints) {
+        checkArgument(constraints.size() > 1);
+        this.constraints = constraints;
     }
 
-    public ConstraintImpl getConstraint1() {
-        return constraint1;
+    public OrImpl(ConstraintImpl constraint1, ConstraintImpl constraint2) {
+        this(Arrays.asList(constraint1, constraint2));
     }
 
-    public ConstraintImpl getConstraint2() {
-        return constraint2;
+    public List<ConstraintImpl> getConstraints() {
+        return constraints;
     }
-    
+
     @Override
     public ConstraintImpl simplify() {
-        constraint1 = constraint1.simplify();
-        constraint2 = constraint2.simplify();
-        if (constraint1.equals(constraint2)) {
-            return constraint1;
+        // Use LinkedHashSet to eliminate duplicate constraints while keeping
+        // the ordering for test cases (and clients?) that depend on it
+        LinkedHashSet<ConstraintImpl> simplified = newLinkedHashSet();
+        boolean changed = false; // keep track of changes in simplification
+
+        for (ConstraintImpl constraint : constraints) {
+            ConstraintImpl simple = constraint.simplify();
+            if (simple instanceof OrImpl) {
+                // unwind nested OR constraints
+                simplified.addAll(((OrImpl) simple).constraints);
+                changed = true;
+            } else if (simplified.add(simple)) {
+                // check if this constraint got simplified
+                changed = changed || simple != constraint;
+            } else {
+                // this constraint was a duplicate of a previous one
+                changed = true;
+            }
+        }
+
+        LinkedHashMap<DynamicOperandImpl, LinkedHashSet<StaticOperandImpl>> in =
+                newLinkedHashMap();
+        Iterator<ConstraintImpl> iterator = simplified.iterator();
+        while (iterator.hasNext()) {
+            ConstraintImpl simple = iterator.next();
+            if (simple instanceof ComparisonImpl
+                    && ((ComparisonImpl) simple).getOperator() == EQUAL) {
+                DynamicOperandImpl o = ((ComparisonImpl) simple).getOperand1();
+                LinkedHashSet<StaticOperandImpl> values = in.get(o);
+                if (values == null) {
+                    values = newLinkedHashSet();
+                    in.put(o, values);
+                }
+                values.add(((ComparisonImpl) simple).getOperand2());
+                iterator.remove();
+                changed = true;
+            } else if (simple instanceof InImpl) {
+                DynamicOperandImpl o = ((InImpl) simple).getOperand1();
+                LinkedHashSet<StaticOperandImpl> values = in.get(o);
+                if (values == null) {
+                    values = newLinkedHashSet();
+                    in.put(o, values);
+                }
+                values.addAll(((InImpl) simple).getOperand2());
+                iterator.remove();
+                changed = true;
+            }
+        }
+        for (Entry<DynamicOperandImpl, LinkedHashSet<StaticOperandImpl>> entry
+                : in.entrySet()) {
+            LinkedHashSet<StaticOperandImpl> values = entry.getValue();
+            if (values.size() == 1) {
+                simplified.add(new ComparisonImpl(
+                        entry.getKey(), EQUAL, values.iterator().next()));
+            } else {
+                simplified.add(new InImpl(
+                        entry.getKey(), newArrayList(values)));
+            }
+        }
+
+        if (simplified.size() == 1) {
+            return simplified.iterator().next();
+        } else if (changed) {
+            return new OrImpl(newArrayList(simplified));
+        } else {
+            return this;
         }
-        return this;
     }
-    
+
     @Override
     public Set<PropertyExistenceImpl> getPropertyExistenceConditions() {
         // for the condition "x=1 or x=2", the existence condition
         // "x is not null" be be derived
-        Set<PropertyExistenceImpl> s1 = constraint1.getPropertyExistenceConditions();
-        if (s1.isEmpty()) {
-            return s1;
-        }
-        Set<PropertyExistenceImpl> s2 = constraint2.getPropertyExistenceConditions();
-        if (s2.isEmpty()) {
-            return s2;
+        Set<PropertyExistenceImpl> result = null;
+        for (ConstraintImpl constraint : constraints) {
+            if (result == null) {
+                result = newHashSet(constraint.getPropertyExistenceConditions());
+            } else {
+                result.retainAll(constraint.getPropertyExistenceConditions());
+            }
         }
-        return Sets.intersection(s1, s2);
+        return result;
     }
     
     @Override
     public FullTextExpression getFullTextConstraint(SelectorImpl s) {
-        FullTextExpression f1 = constraint1.getFullTextConstraint(s);
-        FullTextExpression f2 = constraint2.getFullTextConstraint(s);
-        if (f1 == null || f2 == null) {
-            // the full-text index can not be used for conditions of the form
-            // "contains(a, 'x') or b=123"
-            return null;
-        }
-        ArrayList<FullTextExpression> list = new ArrayList<FullTextExpression>();
-        list.add(f1);
-        list.add(f2);
+        List<FullTextExpression> list = newArrayList();
+        for (ConstraintImpl constraint : constraints) {
+            FullTextExpression expression = constraint.getFullTextConstraint(s);
+            if (expression != null) {
+                list.add(expression);
+            } else {
+                // the full-text index can not be used for conditions
+                // of the form "contains(a, 'x') or b=123"
+                return null;
+            }
+        }
         return new FullTextOr(list);
     }
     
     @Override
     public Set<SelectorImpl> getSelectors() {
-        Set<SelectorImpl> s1 = constraint1.getSelectors();
-        Set<SelectorImpl> s2 = constraint2.getSelectors();
-        if (s1.isEmpty()) {
-            return s2;
-        } else if (s2.isEmpty()) {
-            return s1;
-        }
-        return Sets.union(s1, s2);
-    }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        Map<DynamicOperandImpl, Set<StaticOperandImpl>> m1 = constraint1.getInMap();
-        Map<DynamicOperandImpl, Set<StaticOperandImpl>> m2 = constraint2.getInMap();
-        if (m1.isEmpty()) {
-            return m1;
-        } else if (m2.isEmpty()) {
-            return m2;
-        }
-        Map<DynamicOperandImpl, Set<StaticOperandImpl>> result = Maps.newHashMap();
-        for (Entry<DynamicOperandImpl, Set<StaticOperandImpl>> e2 : m2.entrySet()) {
-            Set<StaticOperandImpl> l2 = e2.getValue();
-            Set<StaticOperandImpl> l1 = m1.get(e2.getKey());
-            if (l1 != null && !l1.isEmpty() && !l2.isEmpty()) {
-                Set<StaticOperandImpl> list = Sets.union(l1, l2);
-                result.put(e2.getKey(), list);
-            }
+        Set<SelectorImpl> result = newHashSet();
+        for (ConstraintImpl constraint : constraints) {
+            result.addAll(constraint.getSelectors());
         }
         return result;
-    }    
+    }
 
     @Override
     public boolean evaluate() {
-        return constraint1.evaluate() || constraint2.evaluate();
+        for (ConstraintImpl constraint : constraints) {
+            if (constraint.evaluate()) {
+                return true;
+            }
+        }
+        return false;
     }
 
+
     @Override
     boolean accept(AstVisitor v) {
         return v.visit(this);
     }
 
     @Override
-    public String toString() {
-        return protect(constraint1) + " or " + protect(constraint2);
-    }
-
-    @Override
     public void restrict(FilterImpl f) {
         Set<PropertyExistenceImpl> set = getPropertyExistenceConditions();
         if (!set.isEmpty()) {
@@ -154,26 +199,6 @@ public class OrImpl extends ConstraintIm
         restrictPushDownNotExists(s);
         restrictPushDownInList(s);
     }
-    
-    /**
-     * Push down the "property in(1, 2, 3)" conditions to the selector, if there
-     * are any that can be derived.
-     * 
-     * @param s the selector
-     */
-    private void restrictPushDownInList(SelectorImpl s) {
-        if (isOnlySelector(s)) {
-            Map<DynamicOperandImpl, Set<StaticOperandImpl>> m = getInMap();
-            for (Entry<DynamicOperandImpl, Set<StaticOperandImpl>> e : m.entrySet()) {
-                Set<StaticOperandImpl> set = e.getValue();
-                if (set.size() > 1) {
-                    InImpl in = new InImpl(e.getKey(), Lists.newArrayList(set));
-                    in.setQuery(query);
-                    in.restrictPushDown(s);
-                }
-            }
-        }
-    }
 
     /**
      * Push down the "not exists" conditions to the selector.
@@ -191,31 +216,108 @@ public class OrImpl extends ConstraintIm
     }
 
     /**
-     * Check whether there are no other selectors in this "or" condition.
+     * Push down the "property in(1, 2, 3)" conditions to the selector, if there
+     * are any that can be derived.
      * 
      * @param s the selector
-     * @return true if there are no other selectors
      */
-    private boolean isOnlySelector(SelectorImpl s) {
-        Set<SelectorImpl> set = getSelectors();
-        if (set.size() == 0) {
-            // conditions without selectors, for example "1=0":
-            // the condition can be pushed down; 
-            // (currently there are no such conditions,
-            // but in the future we might add them)
+    private void restrictPushDownInList(SelectorImpl s) {
+        DynamicOperandImpl operand = null;
+        LinkedHashSet<StaticOperandImpl> values = newLinkedHashSet();
+ 
+        List<AndImpl> ands = newArrayList();
+        for (ConstraintImpl constraint : constraints) {
+            Set<SelectorImpl> selectors = constraint.getSelectors();
+            if (selectors.size() != 1 || !selectors.contains(s)) {
+                return;
+            } else if (constraint instanceof AndImpl) {
+                ands.add((AndImpl) constraint);
+            } else if (constraint instanceof InImpl) {
+                InImpl in = (InImpl) constraint;
+                DynamicOperandImpl o = in.getOperand1();
+                if (operand == null || operand.equals(o)) {
+                    operand = o;
+                    values.addAll(in.getOperand2());
+                } else {
+                    return;
+                }
+            } else if (constraint instanceof ComparisonImpl
+                    && ((ComparisonImpl) constraint).getOperator() == EQUAL) {
+                ComparisonImpl comparison = (ComparisonImpl) constraint;
+                DynamicOperandImpl o = comparison.getOperand1();
+                if (operand == null || operand.equals(o)) {
+                    operand = o;
+                    values.add(comparison.getOperand2());
+                } else {
+                    return;
+                }
+            } else {
+                return;
+            }
+        }
+
+        if (operand == null) {
+            return;
+        }
+
+        for (AndImpl and : ands) {
+            boolean found = false;
+            for (ConstraintImpl constraint : and.getConstraints()) {
+                if (constraint instanceof InImpl) {
+                    InImpl in = (InImpl) constraint;
+                    if (operand.equals(in.getOperand1())) {
+                        values.addAll(in.getOperand2());
+                        found = true;
+                        break;
+                    }
+                } else if (constraint instanceof ComparisonImpl
+                        && ((ComparisonImpl) constraint).getOperator() == EQUAL) {
+                    ComparisonImpl comparison = (ComparisonImpl) constraint;
+                    if (operand.equals(comparison.getOperand1())) {
+                        values.add(comparison.getOperand2());
+                        found = true;
+                        break;
+                    }
+                }
+            }
+            if (!found) {
+                return;
+            }
+        }
+
+        InImpl in = new InImpl(operand, newArrayList(values));
+        in.setQuery(query);
+        in.restrictPushDown(s);
+    }
+
+    //------------------------------------------------------------< Object >--
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        for (ConstraintImpl constraint : constraints) {
+            if (builder.length() > 0) {
+                builder.append(" or ");
+            }
+            builder.append(protect(constraint));
+        }
+        return builder.toString();
+    }
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
             return true;
-        } else if (set.size() > 1) {
-            // "x.a=1 or y.a=2" can't be pushed down to either "x" or "y"
-            return false;
+        } else if (that instanceof OrImpl) {
+            return constraints.equals(((OrImpl) that).constraints);
         } else {
-            // exactly one selector: check if it's the right one
-            SelectorImpl s2 = set.iterator().next();
-            if (!s2.equals(s)) {
-                // a different selector
-                return false;
-            }
+            return false;
         }
-        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return constraints.hashCode();
     }
 
 }

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=1610723&r1=1610722&r2=1610723&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 Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
@@ -58,11 +57,6 @@ public class PropertyExistenceImpl exten
     public Set<SelectorImpl> getSelectors() {
         return Collections.singleton(selector);
     }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }  
 
     @Override
     boolean accept(AstVisitor v) {

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=1610723&r1=1610722&r2=1610723&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 Tue Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.Tree;
@@ -86,11 +85,6 @@ public class PropertyInexistenceImpl ext
     public Set<SelectorImpl> getSelectors() {
         return Collections.singleton(selector);
     }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }  
 
     @Override
     boolean accept(AstVisitor v) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SameNodeImpl.java Tue Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
@@ -54,11 +53,6 @@ public class SameNodeImpl extends Constr
     public Set<SelectorImpl> getSelectors() {
         return Collections.singleton(selector);
     }
-    
-    @Override 
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }  
 
     @Override
     boolean accept(AstVisitor v) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SimilarImpl.java Tue Jul 15 15:17:01 2014
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
@@ -93,11 +92,6 @@ public class SimilarImpl extends Constra
     }
 
     @Override
-    public Map<DynamicOperandImpl, Set<StaticOperandImpl>> getInMap() {
-        return Collections.emptyMap();
-    }
-
-    @Override
     public void restrict(FilterImpl f) {
         if (f.getSelector().equals(selector)) {
             PropertyValue p = pathExpression.currentValue();

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextAnd.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextAnd.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextAnd.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextAnd.java Tue Jul 15 15:17:01 2014
@@ -19,6 +19,7 @@
 package org.apache.jackrabbit.oak.query.fulltext;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -26,9 +27,9 @@ import java.util.Set;
  */
 public class FullTextAnd extends FullTextExpression {
     
-    public final ArrayList<FullTextExpression> list;
+    public final List<FullTextExpression> list;
     
-    public FullTextAnd(ArrayList<FullTextExpression> list) {
+    public FullTextAnd(List<FullTextExpression> list) {
         this.list = list;
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextOr.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextOr.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextOr.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextOr.java Tue Jul 15 15:17:01 2014
@@ -29,9 +29,9 @@ import java.util.TreeSet;
  */
 public class FullTextOr extends FullTextExpression {
     
-    public final ArrayList<FullTextExpression> list;
+    public final List<FullTextExpression> list;
     
-    public FullTextOr(ArrayList<FullTextExpression> list) {
+    public FullTextOr(List<FullTextExpression> list) {
         this.list = list;
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/SimpleExcerptProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/SimpleExcerptProvider.java?rev=1610723&r1=1610722&r2=1610723&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/SimpleExcerptProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/SimpleExcerptProvider.java Tue Jul 15 15:17:01 2014
@@ -115,14 +115,14 @@ public class SimpleExcerptProvider {
             }
         }
         if (c instanceof AndImpl) {
-            AndImpl a = (AndImpl) c;
-            tokens.addAll(extractFulltext(a.getConstraint1()));
-            tokens.addAll(extractFulltext(a.getConstraint2()));
+            for (ConstraintImpl constraint : ((AndImpl) c).getConstraints()) {
+                tokens.addAll(extractFulltext(constraint));
+            }
         }
         if (c instanceof OrImpl) {
-            OrImpl o = (OrImpl) c;
-            tokens.addAll(extractFulltext(o.getConstraint1()));
-            tokens.addAll(extractFulltext(o.getConstraint2()));
+            for (ConstraintImpl constraint : ((OrImpl) c).getConstraints()) {
+                tokens.addAll(extractFulltext(constraint));
+            }
         }
         return tokens;
     }

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ast/SimplifyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ast/SimplifyTest.java?rev=1610723&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ast/SimplifyTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ast/SimplifyTest.java Tue Jul 15 15:17:01 2014
@@ -0,0 +1,79 @@
+/*
+ * 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.ast;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
+import org.junit.Test;
+
+/**
+ * Tests constraint simplification
+ */
+public class SimplifyTest {
+
+    private static final StaticOperandImpl A =
+            new LiteralImpl(PropertyValues.newString("A"));
+
+    private static final StaticOperandImpl B =
+            new LiteralImpl(PropertyValues.newString("B"));
+
+    private static final StaticOperandImpl C =
+            new LiteralImpl(PropertyValues.newString("C"));
+
+    private static final PropertyValueImpl foo =
+            new PropertyValueImpl("a", "foo");
+
+    private static final ComparisonImpl fooIsA =
+            new ComparisonImpl(foo, Operator.EQUAL, A);
+
+    private static final ComparisonImpl fooIsB =
+            new ComparisonImpl(foo, Operator.EQUAL, B);
+
+    private static final ComparisonImpl fooIsC =
+            new ComparisonImpl(foo, Operator.EQUAL, C);
+
+    private static final InImpl fooInAB = new InImpl(foo, Arrays.asList(A, B));
+
+    private static final InImpl fooInBC = new InImpl(foo, Arrays.asList(B, C));
+
+    private static final InImpl fooInABC = new InImpl(foo, Arrays.asList(A, B, C));
+
+    @Test
+    public void simplifyIn() {
+        assertEquals(fooIsA, new InImpl(foo, Arrays.asList(A)).simplify());
+        assertEquals(fooIsA, new InImpl(foo, Arrays.asList(A, A)).simplify());
+        assertEquals(fooInAB, new InImpl(foo, Arrays.asList(A, A, B)).simplify());
+    }
+
+    @Test
+    public void simplifyOr() {
+        assertEquals(fooIsA, new OrImpl(fooIsA, fooIsA).simplify());
+        assertEquals(fooInAB, new OrImpl(fooIsA, fooIsB).simplify());
+        assertEquals(fooInBC, new OrImpl(fooIsB, fooIsC).simplify());
+        assertEquals(fooInABC, new OrImpl(fooInAB, fooIsC).simplify());
+        assertEquals(fooInABC, new OrImpl(fooIsA, fooInBC).simplify());
+        assertEquals(fooInABC, new OrImpl(fooInAB, fooInBC).simplify());
+        assertEquals(fooInABC, new OrImpl(fooIsA, fooInABC).simplify());
+        assertEquals(fooInABC, new OrImpl(fooIsA, new OrImpl(fooIsB, fooIsC)).simplify());
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/ast/SimplifyTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=1610723&r1=1610722&r2=1610723&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 Tue Jul 15 15:17:01 2014
@@ -43,8 +43,7 @@ explain select e.[jcr:path]
   and name(d) = 'e'
   and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4')
 [nt:base] as [e] /* property jcr:uuid
-  where ([e].[jcr:uuid] is not null)
-  and ([e].[jcr:uuid] in('1', '2', '3', '4')) */
+  where [e].[jcr:uuid] in('1', '2', '3', '4') */
   inner join [nt:base] as [d] /* traverse "* && //parent/of/join"
   where name([d]) = 'e' */
   on ischildnode([e], [d])
@@ -72,8 +71,7 @@ explain select e.[jcr:path]
   and name(d) = 'e'
   and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4')
 [nt:base] as [e] /* property jcr:uuid
-  where ([e].[jcr:uuid] is not null)
-  and ([e].[jcr:uuid] in('1', '2', '3', '4')) */
+  where [e].[jcr:uuid] in('1', '2', '3', '4') */
   inner join [nt:base] as [d] /* traverse "* && //parent/of/join"
   where name([d]) = 'e' */
   on ischildnode([e], [d])
@@ -118,8 +116,7 @@ explain select b.[jcr:uuid]
   inner join [nt:base] as b on isdescendantnode(b, a)
   where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2')
 [nt:base] as [a] /* property jcr:uuid
-  where ([a].[jcr:uuid] is not null)
-  and ([a].[jcr:uuid] in('1', '2')) */
+  where [a].[jcr:uuid] in('1', '2') */
   inner join [nt:base] as [b] /* traverse "* && //path/from/join//*" */
   on isdescendantnode([b], [a])
 
@@ -129,8 +126,7 @@ explain select b.[jcr:uuid]
   where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2')
   and b.[jcr:uuid] is not null
 [nt:base] as [a] /* property jcr:uuid
-  where ([a].[jcr:uuid] is not null)
-  and ([a].[jcr:uuid] in('1', '2')) */
+  where [a].[jcr:uuid] in('1', '2') */
   inner join [nt:base] as [b] /* property jcr:uuid
   where [b].[jcr:uuid] is not null */
   on isdescendantnode([b], [a])
@@ -159,8 +155,7 @@ explain select *
   from [nt:base]
   where [jcr:uuid] = '1' or [jcr:uuid] = '2'
 [nt:base] as [nt:base] /* property jcr:uuid
-  where ([nt:base].[jcr:uuid] is not null)
-  and ([nt:base].[jcr:uuid] in('1', '2')) */
+  where [nt:base].[jcr:uuid] in('1', '2') */
 
 explain select *
   from [nt:base]
@@ -191,11 +186,9 @@ explain select *
   where (a.[jcr:uuid]=1 or a.[jcr:uuid]=2)
   and (b.[jcr:uuid]=3 or b.[jcr:uuid]=4)
 [nt:base] as [a] /* property jcr:uuid
-  where ([a].[jcr:uuid] is not null)
-  and ([a].[jcr:uuid] in(1, 2)) */
+  where [a].[jcr:uuid] in(1, 2) */
   inner join [nt:base] as [b] /* property jcr:uuid
-  where ([b].[jcr:uuid] is not null)
-  and ([b].[jcr:uuid] in(3, 4)) */
+  where [b].[jcr:uuid] in(3, 4) */
   on isdescendantnode([b], [a])
 
 explain select *