Author: thomasm
Date: Thu Feb 20 07:18:24 2014
New Revision: 1570093

URL: http://svn.apache.org/r1570093
Log:
OAK-1432 Query: use "union" for complex XPath queries that use multiple "or"

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
    
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java?rev=1570093&r1=1570092&r2=1570093&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java
 Thu Feb 20 07:18:24 2014
@@ -42,7 +42,7 @@ abstract class Expression {
         } else if (add == null) {
             return old;
         }
-        return new Expression.Condition(old, "and", add, 
Expression.PRECEDENCE_AND);
+        return new Expression.AndCondition(old, add);
     }
     
     /**
@@ -55,6 +55,15 @@ abstract class Expression {
     }
     
     /**
+     * Pull an OR condition up to the right hand side of an AND condition.
+     * 
+     * @return the (possibly rotated) expression
+     */
+    Expression pullOrRight() {
+        return this;
+    }
+    
+    /**
      * Get the operator / operation precedence. The JCR specification uses:
      * 1=OR, 2=AND, 3=condition, 4=operand  
      * 
@@ -255,6 +264,27 @@ abstract class Expression {
             super(left, "and", right, Expression.PRECEDENCE_AND);
         }
         
+        @Override
+        AndCondition pullOrRight() {
+            if (right instanceof OrCondition) {
+                return this;
+            } else if (left instanceof OrCondition) {
+                return new AndCondition(right, left);
+            }
+            if (right instanceof AndCondition) {
+                // pull up x:
+                // a and (b and (x)) -> (a and b) and (x)
+                AndCondition r2 = (AndCondition) right;
+                r2 = r2.pullOrRight();
+                AndCondition l2 = new AndCondition(left, r2.left);
+                l2 = l2.pullOrRight();
+                return new AndCondition(l2, r2.right);
+            } else if (left instanceof AndCondition) {
+                return new AndCondition(right, left).pullOrRight();
+            }
+            return this;
+        }
+        
     }
     
     /**

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java?rev=1570093&r1=1570092&r2=1570093&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
 Thu Feb 20 07:18:24 2014
@@ -58,8 +58,34 @@ public class Statement {
         if (where == null) {
             return this;
         }
-        if (where instanceof OrCondition) {
-            OrCondition or = (OrCondition) where;
+        ArrayList<Expression> unionList = new ArrayList<Expression>();
+        addToUnionList(where, unionList);
+        if (unionList.size() == 1) {
+            return this;
+        }
+        Statement union = null;
+        for (int i = 0; i < unionList.size(); i++) {
+            Expression e = unionList.get(i);
+            Statement s = new Statement();
+            s.columnSelector = columnSelector;
+            s.selectors = selectors;
+            s.columnList = columnList;
+            s.where = e;
+            if (i == unionList.size() - 1) {
+                s.xpathQuery = xpathQuery;
+            }
+            if (union == null) {
+                union = s;
+            } else {
+                union = new UnionStatement(union.optimize(), s.optimize());
+            }
+        }
+        return union;
+    }
+    
+    private static void addToUnionList(Expression condition,  
ArrayList<Expression> unionList) {
+        if (condition instanceof OrCondition) {
+            OrCondition or = (OrCondition) condition;
             if (or.getCommonLeftPart() != null) {
                 // @x = 1 or @x = 2 
                 // is automatically converted to 
@@ -72,29 +98,17 @@ public class Statement {
                 // @x = 1 or @y = 2
                 // or similar are converted to
                 // (@x = 1) union (@y = 2)
-                Statement s1 = new Statement();
-                s1.columnSelector = columnSelector;
-                s1.selectors = selectors;
-                s1.columnList = columnList;
-                s1.where = or.left;
-                Statement s2 = new Statement();
-                s2.columnSelector = columnSelector;
-                s2.selectors = selectors;
-                s2.columnList = columnList;
-                s2.where = or.right;
-                s2.xpathQuery = xpathQuery;
-                return new UnionStatement(s1.optimize(), s2.optimize());
+                addToUnionList(or.left, unionList);
+                addToUnionList(or.right, unionList);
+                return;
             }
-        } else if (where instanceof AndCondition) {
+        } else if (condition instanceof AndCondition) {
             // 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)
-            AndCondition and = (AndCondition) where;
-            if (and.left instanceof OrCondition && !(and.right instanceof 
OrCondition)) {
-                // swap left and right
-                and = new AndCondition(and.right, and.left);
-            }
+            AndCondition and = (AndCondition) condition;
+            and = and.pullOrRight();
             if (and.right instanceof OrCondition) {
                 OrCondition or = (OrCondition) and.right;
                 if (or.getCommonLeftPart() != null) {
@@ -106,23 +120,13 @@ public class Statement {
                     // do not optimize "contains"
                 } else {
                     // same as above, but with the added "and"
-                    // TODO avoid code duplication if possible
-                    Statement s1 = new Statement();
-                    s1.columnSelector = columnSelector;
-                    s1.selectors = selectors;
-                    s1.columnList = columnList;
-                    s1.where = new AndCondition(and.left, or.left);
-                    Statement s2 = new Statement();
-                    s2.columnSelector = columnSelector;
-                    s2.selectors = selectors;
-                    s2.columnList = columnList;
-                    s2.where = new AndCondition(and.left, or.right);
-                    s2.xpathQuery = xpathQuery;
-                    return new UnionStatement(s1.optimize(), s2.optimize());
+                    addToUnionList(new AndCondition(and.left, or.left), 
unionList);
+                    addToUnionList(new AndCondition(and.left, or.right), 
unionList);
+                    return;
                 }
             }
         }
-        return this;
+        unionList.add(condition);
     }
     
     @Override

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt?rev=1570093&r1=1570092&r2=1570093&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt
 Thu Feb 20 07:18:24 2014
@@ -24,8 +24,11 @@
 # * use ascii character only
 
 # "or" problem (OAK-1432)
-# xpath2sql /jcr:root/content/dam//element(*, 
nt:unstructured)[((@sling:resourceType = 'dam/smartcollection' or 
@sling:resourceType = 'dam/collection') or @sling:resourceSuperType = 
'dam/collection')]
-# select
+xpath2sql /jcr:root/content//element(*, nt:unstructured)[((@p1 = 'x1' or @p2 = 
'x2') or @p3 = 'x3')]
+select [jcr:path], [jcr:score], * from [nt:unstructured] as a where 
isdescendantnode(a, '/content') and [p1] = 'x1' union select [jcr:path], 
[jcr:score], * from [nt:unstructured] as a where isdescendantnode(a, 
'/content') and [p2] = 'x2' union select [jcr:path], [jcr:score], * from 
[nt:unstructured] as a where isdescendantnode(a, '/content') and [p3] = 'x3' /* 
xpath: /jcr:root/content//element(*, nt:unstructured)[((@p1 = 'x1' or @p2 = 
'x2') or @p3 = 'x3')] */
+
+xpath2sql //*[((@jcr:primaryType = 'nt:unstructured' and (@p1 = 'x' or @p2 = 
'x')) or (@p3 = 'x' and @jcr:primaryType = 'nt:folder')) ]
+select [jcr:path], [jcr:score], * from [nt:base] as a where [jcr:primaryType] 
= 'nt:unstructured' and [p1] = 'x' union select [jcr:path], [jcr:score], * from 
[nt:base] as a where [jcr:primaryType] = 'nt:unstructured' and [p2] = 'x' union 
select [jcr:path], [jcr:score], * from [nt:base] as a where [p3] = 'x' and 
[jcr:primaryType] = 'nt:folder' /* xpath: //*[((@jcr:primaryType = 
'nt:unstructured' and (@p1 = 'x' or @p2 = 'x')) or (@p3 = 'x' and 
@jcr:primaryType = 'nt:folder')) ] */
 
 # property names with missing @
 
@@ -38,7 +41,7 @@ select [jcr:path], [jcr:score], * from [
 # wildcards in relative property paths
 
 xpath2sql /jcr:root/etc/test//*[@size='M' or */@size='M']
-select [jcr:path], [jcr:score], * from [nt:base] as a where ([size] = 'M' or 
[*/size] = 'M') and isdescendantnode(a, '/etc/test') /* xpath: 
/jcr:root/etc/test//*[@size='M' or */@size='M'] */
+select [jcr:path], [jcr:score], * from [nt:base] as a where 
isdescendantnode(a, '/etc/test') and [size] = 'M' union select [jcr:path], 
[jcr:score], * from [nt:base] as a where isdescendantnode(a, '/etc/test') and 
[*/size] = 'M' /* xpath: /jcr:root/etc/test//*[@size='M' or */@size='M'] */
 
 # union (complex)
 
@@ -220,10 +223,10 @@ xpath2sql //element(*,rep:Authorizable)[
 select [jcr:path], [jcr:score], * from [rep:Authorizable] as a where 
contains([profile/givenName/*], '**') or contains([profile/familyName/*], '**') 
or contains([profile/email/*], '**') or [rep:principalName] like '%%' or 
name(a) like '%%' order by [rep:principalName] /* xpath: 
//element(*,rep:Authorizable)[(((jcr:contains(profile/givenName,'**') or 
jcr:contains(profile/familyName,'**')) or jcr:contains(profile/email,'**')) or 
(jcr:like(@rep:principalName,'%%') or jcr:like(fn:name(.),'%%')))] order by 
@rep:principalName ascending */
 
 xpath2sql //*[@a=1 or @b=1]/sub[@c=1]
-select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from 
[nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where (a.[a] = 1 
or a.[b] = 1) and b.[c] = 1 and name(b) = 'sub' /* xpath: //*[@a=1 or 
@b=1]/sub[@c=1] */
+select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from 
[nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where b.[c] = 1 
and name(b) = 'sub' and a.[a] = 1 union select b.[jcr:path] as [jcr:path], 
b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b 
on ischildnode(b, a) where b.[c] = 1 and name(b) = 'sub' and a.[b] = 1 /* 
xpath: //*[@a=1 or @b=1]/sub[@c=1] */
 
 xpath2sql //*[@a=1 or @b=1]/sub[@c=1 or @d=1]
-select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from 
[nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where (a.[a] = 1 
or a.[b] = 1) and (b.[c] = 1 or b.[d] = 1) and name(b) = 'sub' /* xpath: 
//*[@a=1 or @b=1]/sub[@c=1 or @d=1] */
+select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from 
[nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where a.[a] = 1 
and name(b) = 'sub' and b.[c] = 1 union select b.[jcr:path] as [jcr:path], 
b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b 
on ischildnode(b, a) where a.[a] = 1 and name(b) = 'sub' and b.[d] = 1 union 
select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from 
[nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where a.[b] = 1 
and name(b) = 'sub' and b.[c] = 1 union select b.[jcr:path] as [jcr:path], 
b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b 
on ischildnode(b, a) where a.[b] = 1 and name(b) = 'sub' and b.[d] = 1 /* 
xpath: //*[@a=1 or @b=1]/sub[@c=1 or @d=1] */
 
 xpath2sql 
//element(*,rep:Authorizable)[(((jcr:contains(profile/@givenName,'**') or 
jcr:contains(profile/@familyName,'**')) or jcr:contains(profile/@email,'**')) 
or (jcr:like(@rep:principalName,'%%') or jcr:like(fn:name(.),'%%')))] order by 
@rep:principalName ascending
 select [jcr:path], [jcr:score], * from [rep:Authorizable] as a where 
contains([profile/givenName], '**') or contains([profile/familyName], '**') or 
contains([profile/email], '**') or [rep:principalName] like '%%' or name(a) 
like '%%' order by [rep:principalName] /* xpath: 
//element(*,rep:Authorizable)[(((jcr:contains(profile/@givenName,'**') or 
jcr:contains(profile/@familyName,'**')) or jcr:contains(profile/@email,'**')) 
or (jcr:like(@rep:principalName,'%%') or jcr:like(fn:name(.),'%%')))] order by 
@rep:principalName ascending */
@@ -369,7 +372,7 @@ xpath2sql /jcr:root/content//*[(@sling:r
 select [jcr:path], [jcr:score], * from [nt:base] as a where 
[sling:resourceType] = 'page' and isdescendantnode(a, '/content') /* xpath: 
/jcr:root/content//*[(@sling:resourceType = 'page')] */
 
 xpath2sql /jcr:root/content//*[@offTime > 
xs:dateTime('2012-03-28T15:56:18.327+02:00') or @onTime > 
xs:dateTime('2012-03-28T15:56:18.327+02:00')]
-select [jcr:path], [jcr:score], * from [nt:base] as a where ([offTime] > 
cast('2012-03-28T15:56:18.327+02:00' as date) or [onTime] > 
cast('2012-03-28T15:56:18.327+02:00' as date)) and isdescendantnode(a, 
'/content') /* xpath: /jcr:root/content//*[@offTime > 
xs:dateTime('2012-03-28T15:56:18.327+02:00') or @onTime > 
xs:dateTime('2012-03-28T15:56:18.327+02:00')] */
+select [jcr:path], [jcr:score], * from [nt:base] as a where 
isdescendantnode(a, '/content') and [offTime] > 
cast('2012-03-28T15:56:18.327+02:00' as date) union select [jcr:path], 
[jcr:score], * from [nt:base] as a where isdescendantnode(a, '/content') and 
[onTime] > cast('2012-03-28T15:56:18.327+02:00' as date) /* xpath: 
/jcr:root/content//*[@offTime > xs:dateTime('2012-03-28T15:56:18.327+02:00') or 
@onTime > xs:dateTime('2012-03-28T15:56:18.327+02:00')] */
 
 xpath2sql /jcr:root/content/campaigns//*[@jcr:primaryType='Page'] order by 
jcr:content/@lastModified descending
 select [jcr:path], [jcr:score], * from [nt:base] as a where [jcr:primaryType] 
= 'Page' and isdescendantnode(a, '/content/campaigns') order by 
[jcr:content/lastModified] desc /* xpath: 
/jcr:root/content/campaigns//*[@jcr:primaryType='Page'] order by 
jcr:content/@lastModified descending */


Reply via email to