Author: thomasm
Date: Fri Jan 29 09:46:24 2016
New Revision: 1727504

URL: http://svn.apache.org/viewvc?rev=1727504&view=rev
Log:
OAK-1744 GQL queries with jcr:primaryType=x don't use the node type index

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/Selector.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Statement.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java
    
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt
    
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java

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=1727504&r1=1727503&r2=1727504&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
 Fri Jan 29 09:46:24 2016
@@ -142,6 +142,19 @@ abstract class Expression {
     boolean isName() {
         return false;
     }
+    
+    /**
+     * Get the most specific nodetype condition, that is a condition of the 
form
+     * "jcr:primaryType = 'x'". If there are multiple such conditions, only the
+     * most strict one needs to be returned. If there are no, or conflicting
+     * conditions, then null may be returned, meaning no usable condition.
+     * 
+     * @param selectorName the selector name
+     * @return null or the nodetype value
+     */
+    public String getMostSpecificNodeType(String selectorName) {
+        return null;
+    }
 
     /**
      * A literal expression.
@@ -214,6 +227,28 @@ abstract class Expression {
         }
         
         @Override
+        public String getMostSpecificNodeType(String selectorName) {
+            if (!"=".equals(operator)) {
+                return null;
+            }
+            if (!(left instanceof Property)) {
+                return null;
+            }
+            Property p = (Property) left;
+            if (!(right instanceof Literal)) {
+                return null;
+            }
+            Literal l = (Literal) right;
+            if (!"jcr:primaryType".equals(p.name)) {
+                return null;
+            }
+            if (selectorName != null && !selectorName.equals(p.selector.name)) 
{
+                return null;
+            }
+            return l.rawText;
+        }   
+        
+        @Override
         Expression getLeft() {
             return left;
         }
@@ -344,6 +379,15 @@ abstract class Expression {
                 in = new InCondition(in.getLeft(), in.list);
                 return in;
             }
+            if (XPathToSQL2Converter.NODETYPE_UNION) {
+                if (left instanceof Condition) {
+                    Condition c = (Condition) left;
+                    if (c.left instanceof Property && 
+                        "jcr:primaryType".equals(((Property) c.left).name)) {
+                        return this;
+                    }
+                }
+            }
             ArrayList<Expression> list = new ArrayList<Expression>();
             list.addAll(left.getRight());
             list.addAll(right.getRight());
@@ -427,6 +471,15 @@ abstract class Expression {
         }
         
         @Override
+        public String getMostSpecificNodeType(String selectorName) {
+            String nt = left.getMostSpecificNodeType(selectorName);
+            if (nt != null) {
+                return nt;
+            }
+            return right.getMostSpecificNodeType(selectorName);
+        }        
+        
+        @Override
         AndCondition pullOrRight() {
             if (right instanceof OrCondition) {
                 return this;

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java?rev=1727504&r1=1727503&r2=1727504&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Selector.java
 Fri Jan 29 09:46:24 2016
@@ -87,4 +87,20 @@ class Selector {
      */
     Expression joinCondition;
     
+    public Selector() {
+    }
+
+    public Selector(Selector s) {
+        this.name = s.name;
+        this.onlySelector = s.onlySelector;
+        this.nodeType = s.nodeType;
+        this.isChild = s.isChild;
+        this.isParent = s.isParent;
+        this.isDescendant = s.isDescendant;
+        this.path = s.path;
+        this.nodeName = s.nodeName;
+        this.condition = s.condition;
+        this.joinCondition = s.joinCondition;
+    }
+    
 }
\ No newline at end of file

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=1727504&r1=1727503&r2=1727504&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
 Fri Jan 29 09:46:24 2016
@@ -56,6 +56,7 @@ public class Statement {
             return this;
         }
         where = where.optimize();
+        optimizeSelectorNodeTypes();
         ArrayList<Expression> unionList = new ArrayList<Expression>();
         addToUnionList(where, unionList);
         if (unionList.size() == 1) {
@@ -65,8 +66,8 @@ public class Statement {
         for (int i = 0; i < unionList.size(); i++) {
             Expression e = unionList.get(i);
             Statement s = new Statement();
-            s.columnSelector = columnSelector;
-            s.selectors = selectors;
+            s.columnSelector = new Selector(columnSelector);
+            s.selectors = cloneSelectors();
             s.columnList = columnList;
             s.where = e;
             if (union == null) {
@@ -83,6 +84,33 @@ public class Statement {
         return union;
     }
     
+    private ArrayList<Selector> cloneSelectors() {
+        ArrayList<Selector> list = new ArrayList<Selector>();
+        for (Selector s : selectors) {
+            list.add(new Selector(s));
+        }
+        return list;
+    }
+    
+    private void optimizeSelectorNodeTypes() {
+        if (!XPathToSQL2Converter.NODETYPE_OPTIMIZATION) {
+            return;
+        }
+        for (int i = 0; i < selectors.size(); i++) {
+            Selector s = selectors.get(i);
+            if (s.nodeType != null && !"nt:base".equals(s.nodeType)) {
+                // explicit node type: ignore
+                continue;
+            }
+            // only filter by selectorName if there are multiple selectors
+            String selectorName = selectors.size() == 1 ? null : s.name;
+            String nodeType = where.getMostSpecificNodeType(selectorName);
+            if (nodeType != null) {
+                s.nodeType = nodeType;
+            }
+        }
+    }
+    
     private static void addToUnionList(Expression condition,  
ArrayList<Expression> unionList) {
         if (condition instanceof OrCondition) {
             OrCondition or = (OrCondition) condition;

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java?rev=1727504&r1=1727503&r2=1727504&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java
 Fri Jan 29 09:46:24 2016
@@ -29,6 +29,23 @@ import java.util.ArrayList;
  * This class can can convert a XPATH query to a SQL2 query.
  */
 public class XPathToSQL2Converter {
+    
+    /**
+     * Optimize queries of the form "from [nt:base] where [jcr:primaryType] = 
'x'" 
+     * to "from [x] where [jcr:primaryType] = 'x'".
+     * Enabled by default.
+     */
+    public static final boolean NODETYPE_OPTIMIZATION = Boolean.parseBoolean(
+            System.getProperty("oak.xpathNodeTypeOptimization", "true"));
+    
+    /**
+     * Convert queries of the form "where [jcr:primaryType] = 'x' or 
[jcr:primaryType] = 'y'"
+     * to "select ... where [jcr:primaryType] = 'x' union select ... where 
[jcr:primaryType] = 'y'". 
+     * If disabled, only one query with "where [jcr:primaryType] in ('x', 'y') 
is used.
+     * Enabled by default.
+     */
+    public static final boolean NODETYPE_UNION = Boolean.parseBoolean(
+            System.getProperty("oak.xpathNodeTypeUnion", "true"));
 
     static final Logger LOG = 
LoggerFactory.getLogger(XPathToSQL2Converter.class);
 

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=1727504&r1=1727503&r2=1727504&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
 Fri Jan 29 09:46:24 2016
@@ -24,6 +24,29 @@
 # * new tests are typically be added on top, after the syntax docs
 # * use ascii character only
 
+# multiple primary types
+xpath2sql /jcr:root/content//*[jcr:contains(., 'abc') and (((@jcr:primaryType 
= 'page')) or ((@jcr:primaryType = 'asset'))) ]
+select [jcr:path], [jcr:score], *
+  from [page] as a
+  where isdescendantnode(a, '/content')
+  and contains(*, 'abc')
+  and [jcr:primaryType] = 'page'
+  union select [jcr:path], [jcr:score], *
+  from [asset] as a
+  where isdescendantnode(a, '/content')
+  and contains(*, 'abc')
+  and [jcr:primaryType] = 'asset'
+  /* xpath ... */
+
+# single primary type
+xpath2sql /jcr:root/content//*[jcr:contains(., 'abc') and @jcr:primaryType = 
'page']
+select [jcr:path], [jcr:score], *
+  from [page] as a
+  where contains(*, 'abc')
+  and [jcr:primaryType] = 'page'
+  and isdescendantnode(a, '/content')
+  /* xpath ... */
+
 # order by @jcr:score
 xpath2sql /jcr:root/content//*[jcr:contains(., 'x')] order by @jcr:score 
descending
 select [jcr:path], [jcr:score], *
@@ -160,15 +183,15 @@ xpath2sql //*[((@jcr:primaryType = 'nt:u
   (@p1 = 'x' or @p2 = 'x'))
   or (@p3 = 'x' and @jcr:primaryType = 'nt:folder')) ]
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and [p1] = 'x'
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and [p2] = 'x'
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:folder] as a
   where [p3] = 'x'
   and [jcr:primaryType] = 'nt:folder'
   /* xpath ... */
@@ -228,11 +251,11 @@ select [jcr:path], [jcr:score], *
 xpath2sql //*[((@jcr:primaryType = 'nt:unstructured')
   and (@resources = '/data' or @resolved = '/data'))]
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and [resources] = '/data'
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and [resolved] = '/data'
   /* xpath ... */
@@ -241,15 +264,15 @@ xpath2sql //*[(((@jcr:primaryType = 'nt:
   and (@resources = '/data' or @resolved = '/data'))
   or (@content = '/data' and (@jcr:primaryType = 'nt:folder'))) ]
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and [resources] = '/data'
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and [resolved] = '/data'
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:folder] as a
   where [content] = '/data'
   and [jcr:primaryType] = 'nt:folder'
   /* xpath ... */
@@ -259,11 +282,11 @@ select [jcr:path], [jcr:score], *
 xpath2sql //*[((@jcr:primaryType = 'nt:unstructured')
   and (jcr:contains(., 'hello') or jcr:contains(., 'world')))]
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and contains(*, 'hello')
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and contains(*, 'world')
   /* xpath ... */
@@ -274,15 +297,15 @@ xpath2sql //*[(((@jcr:primaryType = 'nt:
   or (@content = '/data'
   and (@jcr:primaryType = 'nt:folder'))) ]
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and contains(*, 'hello')
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and contains(*, 'world')
   union select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:folder] as a
   where [content] = '/data'
   and [jcr:primaryType] = 'nt:folder'
   /* xpath ... */
@@ -597,7 +620,7 @@ select b.[jcr:path] as [jcr:path], b.[jc
 xpath2sql /testRoot/*[@jcr:primaryType='nt:unstructured' and fn:not(@mytext)]
 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
+  inner join [nt:unstructured] as b
   on ischildnode(b, a)
   where name(a) = 'testRoot'
   and issamenode(a, '/')
@@ -805,7 +828,7 @@ xpath2sql //testroot/*[@jcr:primaryType=
   and fn:not(@mytext)]
 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
+  inner join [nt:unstructured] as b
   on ischildnode(b, a)
   where name(a) = 'testroot'
   and b.[jcr:primaryType] = 'nt:unstructured'
@@ -817,7 +840,7 @@ invalid: Query: /jcr:root/testroot/peopl
 
 xpath2sql //*[@jcr:primaryType='nt:unstructured' and jcr:like(@foo,"%ar'ba%")]
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [nt:unstructured] as a
   where [jcr:primaryType] = 'nt:unstructured'
   and [foo] like '%ar''ba%'
   /* xpath ... */
@@ -940,7 +963,7 @@ xpath2sql //testroot/*[@jcr:primaryType=
   order by @prop2, @prop1
 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
+  inner join [nt:unstructured] as b
   on ischildnode(b, a)
   where name(a) = 'testroot'
   and b.[jcr:primaryType] = 'nt:unstructured'
@@ -1070,7 +1093,7 @@ select [jcr:path], [jcr:score], *
 xpath2sql /jcr:root/content/campaigns//*[@jcr:primaryType='Page']
   order by jcr:content/@lastModified descending
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [Page] as a
   where [jcr:primaryType] = 'Page'
   and isdescendantnode(a, '/content/campaigns')
   order by [jcr:content/lastModified] desc
@@ -1168,7 +1191,7 @@ select [jcr:path], [jcr:score], *
 xpath2sql /jcr:root/etc/reports//*[@jcr:primaryType='Page']
   order by jcr:content/@lastModified descending
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [Page] as a
   where [jcr:primaryType] = 'Page'
   and isdescendantnode(a, '/etc/reports')
   order by [jcr:content/lastModified] desc
@@ -1177,7 +1200,7 @@ select [jcr:path], [jcr:score], *
 xpath2sql /jcr:root/etc/segment//*[@jcr:primaryType='Page']
   order by jcr:content/@lastModified descending
 select [jcr:path], [jcr:score], *
-  from [nt:base] as a
+  from [Page] as a
   where [jcr:primaryType] = 'Page'
   and isdescendantnode(a, '/etc/segment')
   order by [jcr:content/lastModified] desc

Modified: 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java?rev=1727504&r1=1727503&r2=1727504&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
 Fri Jan 29 09:46:24 2016
@@ -704,7 +704,7 @@ public class QueryTest extends AbstractR
         session.save();
         Query q = session.getWorkspace().getQueryManager().createQuery(
                 "/jcr:root/etc//*["+
-                        "(@jcr:primaryType = 'a'  or @jcr:primaryType = 'b') "+
+                        "(@jcr:primaryType = 'nt:file'  or @jcr:primaryType = 
'nt:folder') "+
                         "or @nt:resourceType = 'test']", "xpath");
         QueryResult qr = q.execute();
         NodeIterator ni = qr.getNodes();


Reply via email to