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 2016/11/01 09:30:15 UTC

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

Author: thomasm
Date: Tue Nov  1 09:30:15 2016
New Revision: 1767458

URL: http://svn.apache.org/viewvc?rev=1767458&view=rev
Log:
OAK-4681 Automatically convert *all* "or" queries to "union" for SQL-2

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AndImpl.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java?rev=1767458&r1=1767457&r2=1767458&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java Tue Nov  1 09:30:15 2016
@@ -31,6 +31,11 @@ public class QueryEngineSettings impleme
      */
     public static final String SQL2_OPTIMISATION_FLAG = "oak.query.sql2optimisation";
     
+    public static final String SQL2_OPTIMISATION_FLAG_2 = "oak.query.sql2optimisation2";
+    
+    public static final boolean SQL2_OPTIMIZATION_2 = 
+            Boolean.parseBoolean(System.getProperty(SQL2_OPTIMISATION_FLAG_2, "true"));
+
     private static final int DEFAULT_QUERY_LIMIT_IN_MEMORY = 
             Integer.getInteger("oak.queryLimitInMemory", Integer.MAX_VALUE);
     
@@ -52,8 +57,9 @@ public class QueryEngineSettings impleme
     private boolean fullTextComparisonWithoutIndex = 
             DEFAULT_FULL_TEXT_COMPARISON_WITHOUT_INDEX;
     
-    private boolean sql2Optimisation = Boolean.parseBoolean(System.getProperty(SQL2_OPTIMISATION_FLAG, "true"));
-    
+    private boolean sql2Optimisation = 
+            Boolean.parseBoolean(System.getProperty(SQL2_OPTIMISATION_FLAG, "true"));
+
     public QueryEngineSettings() {
     }
     

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=1767458&r1=1767457&r2=1767458&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 Nov  1 09:30:15 2016
@@ -26,10 +26,12 @@ import static org.apache.jackrabbit.oak.
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
@@ -217,11 +219,61 @@ public class AndImpl extends ConstraintI
         }
         return new AndImpl(clone);
     }
+    
+    public void addToUnionList(Set<ConstraintImpl> target) {
+        // conditions of type
+        // @a = 1 and (@x = 1 or @y = 2)
+        // are automatically converted to
+        // (@a = 1 and @x = 1) union (@a = 1 and @y = 2)
+        AndImpl and = pullOrRight();
+        ConstraintImpl last = and.getLastConstraint();
+        if (last instanceof OrImpl) {
+            OrImpl or = (OrImpl) last;
+            // same as above, but with the added "and"
+            for(ConstraintImpl c : or.getConstraints()) {
+                ArrayList<ConstraintImpl> list = and.getFirstConstraints();
+                list.add(c);
+                new AndImpl(list).addToUnionList(target);
+            }
+            return;
+        }
+        target.add(this);
+    }
+    
+    private ArrayList<ConstraintImpl> getFirstConstraints() {
+        ArrayList<ConstraintImpl> list = new ArrayList<ConstraintImpl>(constraints.size() - 1);
+        list.addAll(constraints.subList(0, constraints.size() - 1));
+        return list;
+    }
+    
+    private ConstraintImpl getLastConstraint() {
+        return constraints.get(constraints.size() - 1);
+    }
+    
+    public AndImpl pullOrRight() {
+        if (getLastConstraint() instanceof OrImpl) {
+            return this;
+        }
+        for (int i = 0; i < constraints.size() - 1; i++) {
+            ConstraintImpl c = constraints.get(i);
+            if (c instanceof OrImpl) {
+                ArrayList<ConstraintImpl> list = new ArrayList<ConstraintImpl>();
+                list.addAll(constraints);
+                list.remove(i);
+                list.add(c);
+                return new AndImpl(list);
+            }
+        }
+        return this;
+    }
 
     @Override
     public Set<ConstraintImpl> convertToUnion() {
-        Set<ConstraintImpl> union = Sets.newHashSet();
-        Set<ConstraintImpl> result = Sets.newHashSet();
+        // use linked hash sets where needed, so that the order of queries
+        // within the UNION is always the same (independent of the JVM
+        // implementation)
+        Set<ConstraintImpl> union = Sets.newLinkedHashSet();
+        Set<ConstraintImpl> result = Sets.newLinkedHashSet();
         Set<ConstraintImpl> nonUnion = Sets.newHashSet();
         
         for (ConstraintImpl c : constraints) {
@@ -240,11 +292,19 @@ public class AndImpl extends ConstraintI
                 result.add(new AndImpl(c, right));
             }
         } else {
-            // in this case prefer to be conservative and don't optimize. This could happen when for
+            // This could happen when for
             // example: WHERE (a OR b) AND (c OR d).
-            // This should be translated into a AND c, a AND d, b AND c, b AND d.
+            // This can be translated into a AND c, a AND d, b AND c, b AND d.
+            if (QueryEngineSettings.SQL2_OPTIMIZATION_2) {
+                Set<ConstraintImpl> set = Sets.newLinkedHashSet();
+                addToUnionList(set);
+                if (set.size() == 1) {
+                    // not a union: same condition as before
+                    return Collections.emptySet();
+                }
+                return set;
+            }
         }
-        
         return result;
     }
     

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java?rev=1767458&r1=1767457&r2=1767458&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/SQL2OptimiseQueryTest.java Tue Nov  1 09:30:15 2016
@@ -27,6 +27,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.query.QueryEngineImpl.QuerySelectionMode.CHEAPEST;
 import static org.apache.jackrabbit.oak.query.QueryEngineImpl.QuerySelectionMode.ORIGINAL;
 import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertThat;
@@ -226,6 +227,45 @@ public class SQL2OptimiseQueryTest exten
         assertNotSame(original, optimised);
     }
     
+    /**
+     * ensure that an optimisation is available for the provided queries.
+     * 
+     * @throws ParseException
+     */
+    @Test
+    public void optimiseAndOrAnd() throws ParseException {
+        optimiseAndOrAnd(
+                "select * from [nt:unstructured] as [c] " + 
+                "where ([a]=1 or [b]=2) and ([x]=3 or [y]=4)",
+                "(y = 4) and (b = 2), " + 
+                "(y = 4) and (a = 1), " + 
+                "(x = 3) and (b = 2), " + 
+                "(x = 3) and (a = 1)");
+        optimiseAndOrAnd(
+                "select * from [nt:unstructured] as [c] " + 
+                "where ([a]=1 or [b]=2 or ([c]=3 and [d]=4)) and ([x]=5 or [y]=6)",
+                "(y = 6) and ((c = 3) and (d = 4)), " + 
+                "(y = 6) and (b = 2), " + 
+                "(y = 6) and (a = 1), " + 
+                "(x = 5) and ((c = 3) and (d = 4)), " + 
+                "(x = 5) and (b = 2), " + 
+                "(x = 5) and (a = 1)");
+    }
+    
+    private void optimiseAndOrAnd(String statement, String expected) throws ParseException {
+        SQL2Parser parser = new SQL2Parser(getMappings(), getNodeTypes(), qeSettings);
+        Query original;
+        original = parser.parse(statement, false);
+        assertNotNull(original);
+        String optimized = original.buildAlternativeQuery().toString();
+        optimized = optimized.replaceAll("\\[", "").replaceAll("\\]","");
+        optimized = optimized.replaceAll("select c.jcr:primaryType as c.jcr:primaryType ", "");
+        optimized = optimized.replaceAll("from nt:unstructured as c where ", "");
+        optimized = optimized.replaceAll("c\\.", "");
+        optimized = optimized.replaceAll(" union ", ", ");
+        assertEquals(expected,  optimized);
+    }
+    
     private NamePathMapper getMappings() {
         return new NamePathMapperImpl(
             new LocalNameMapper(root, QueryEngine.NO_MAPPINGS));