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();