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 2014/01/30 10:20:02 UTC
svn commit: r1562751 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/query/ast/
main/java/org/apache/jackrabbit/oak/query/fulltext/
test/java/org/apache/jackrabbit/oak/query/
test/resources/org/apache/jackrabbit/oak/query/
Author: thomasm
Date: Thu Jan 30 09:20:02 2014
New Revision: 1562751
URL: http://svn.apache.org/r1562751
Log:
OAK-1215 Wildcards in relative property paths don't work in search expressions
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/SimpleExcerptProvider.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt
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/ast/AstElement.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java?rev=1562751&r1=1562750&r2=1562751&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElement.java Thu Jan 30 09:20:02 2014
@@ -52,9 +52,10 @@ abstract class AstElement {
/**
* Normalize the property name (including namespace remapping).
+ * Asterisks are kept.
*
* @param propertyName the property name to normalize
- * @return the normalized property name
+ * @return the normalized (oak-) property name
*/
protected String normalizePropertyName(String propertyName) {
// TODO normalize the path (remove superfluous ".." and "."
@@ -71,7 +72,23 @@ abstract class AstElement {
}
// relative properties
String relativePath = PathUtils.getParentPath(propertyName);
- relativePath = query.getOakPath(relativePath);
+ if (relativePath.indexOf('*') >= 0) {
+ StringBuilder buff = new StringBuilder();
+ for (String p : PathUtils.elements(relativePath)) {
+ if (!p.equals("*")) {
+ p = query.getOakPath(p);
+ }
+ if (p.length() > 0) {
+ if (buff.length() > 0) {
+ buff.append('/');
+ }
+ buff.append(p);
+ }
+ }
+ relativePath = buff.toString();
+ } else {
+ relativePath = query.getOakPath(relativePath);
+ }
propertyName = PathUtils.getName(propertyName);
propertyName = normalizeNonRelativePropertyName(propertyName);
return PathUtils.concat(relativePath, propertyName);
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java?rev=1562751&r1=1562750&r2=1562751&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java Thu Jan 30 09:20:02 2014
@@ -18,7 +18,6 @@
*/
package org.apache.jackrabbit.oak.query.ast;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -26,17 +25,10 @@ import java.util.Set;
import javax.jcr.PropertyType;
-import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.PropertyValue;
-import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.query.QueryImpl;
import org.apache.jackrabbit.oak.query.SQL2Parser;
import org.apache.jackrabbit.oak.query.index.FilterImpl;
-import org.apache.jackrabbit.oak.spi.query.PropertyValues;
-
-import com.google.common.collect.Iterables;
/**
* A property expression.
@@ -107,58 +99,13 @@ public class PropertyValueImpl extends D
@Override
public PropertyValue currentProperty() {
- boolean asterisk = PathUtils.getName(propertyName).equals("*");
- if (!asterisk) {
- PropertyValue p = selector.currentProperty(propertyName);
- return matchesPropertyType(p) ? p : null;
- }
- Tree tree = selector.currentTree();
- if (tree == null || !tree.exists()) {
- return null;
- }
- if (!asterisk) {
- String name = PathUtils.getName(propertyName);
- name = normalizePropertyName(name);
- PropertyState p = tree.getProperty(name);
- if (p == null) {
- return null;
- }
- return matchesPropertyType(p) ? PropertyValues.create(p) : null;
- }
- // asterisk - create a multi-value property
- // warning: the returned property state may have a mixed type
- // (not all values may have the same type)
-
- // TODO currently all property values are converted to strings -
- // this doesn't play well with the idea that the types may be different
- List<String> values = new ArrayList<String>();
- for (PropertyState p : tree.getProperties()) {
- if (matchesPropertyType(p)) {
- Iterables.addAll(values, p.getValue(Type.STRINGS));
- }
- }
- // "*"
- return PropertyValues.newString(values);
- }
-
- private boolean matchesPropertyType(PropertyValue value) {
- if (value == null) {
- return false;
- }
- if (propertyType == PropertyType.UNDEFINED) {
- return true;
- }
- return value.getType().tag() == propertyType;
- }
-
- private boolean matchesPropertyType(PropertyState state) {
- if (state == null) {
- return false;
- }
+ PropertyValue p;
if (propertyType == PropertyType.UNDEFINED) {
- return true;
+ p = selector.currentProperty(propertyName);
+ } else {
+ p = selector.currentProperty(propertyName, propertyType);
}
- return state.getType().tag() == propertyType;
+ return p;
}
public void bindSelector(SourceImpl source) {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java?rev=1562751&r1=1562750&r2=1562751&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java Thu Jan 30 09:20:02 2014
@@ -40,6 +40,7 @@ import javax.annotation.Nonnull;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.PropertyValue;
import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.query.QueryImpl;
import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
@@ -53,6 +54,7 @@ import org.apache.jackrabbit.oak.spi.que
import org.apache.jackrabbit.oak.spi.state.NodeState;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
/**
* A selector within a query.
@@ -384,7 +386,20 @@ public class SelectorImpl extends Source
String pn = normalizePropertyName(propertyName);
return currentOakProperty(pn);
}
-
+
+ /**
+ * The value for the given selector for the current node, filtered by
+ * property type.
+ *
+ * @param propertyName the JCR (not normalized) property name
+ * @param propertyType only include properties of this type
+ * @return the property value (possibly null)
+ */
+ public PropertyValue currentProperty(String propertyName, int propertyType) {
+ String pn = normalizePropertyName(propertyName);
+ return currentOakProperty(pn, propertyType);
+ }
+
/**
* Get the property value. The property name may be relative. The special
* property names "jcr:path", "jcr:score" and "rep:excerpt" are supported.
@@ -393,6 +408,24 @@ public class SelectorImpl extends Source
* @return the property value or null if not found
*/
public PropertyValue currentOakProperty(String oakPropertyName) {
+ return currentOakProperty(oakPropertyName, null);
+ }
+
+ private PropertyValue currentOakProperty(String oakPropertyName, Integer propertyType) {
+ boolean asterisk = oakPropertyName.indexOf('*') >= 0;
+ if (asterisk) {
+ Tree t = currentTree();
+ ArrayList<PropertyValue> list = new ArrayList<PropertyValue>();
+ readOakProperties(list, t, oakPropertyName, propertyType);
+ if (list.size() == 0) {
+ return null;
+ }
+ ArrayList<String> strings = new ArrayList<String>();
+ for (PropertyValue p : list) {
+ Iterables.addAll(strings, p.getValue(Type.STRINGS));
+ }
+ return PropertyValues.newString(strings);
+ }
boolean relative = oakPropertyName.indexOf('/') >= 0;
Tree t = currentTree();
if (relative) {
@@ -410,6 +443,11 @@ public class SelectorImpl extends Source
}
oakPropertyName = PathUtils.getName(oakPropertyName);
}
+ return currentOakProperty(t, oakPropertyName, propertyType);
+ }
+
+ private PropertyValue currentOakProperty(Tree t, String oakPropertyName, Integer propertyType) {
+ PropertyValue result;
if (t == null || !t.exists()) {
return null;
}
@@ -420,13 +458,59 @@ public class SelectorImpl extends Source
// not a local path
return null;
}
- return PropertyValues.newString(local);
+ result = PropertyValues.newString(local);
} else if (oakPropertyName.equals(QueryImpl.JCR_SCORE)) {
- return currentRow.getValue(QueryImpl.JCR_SCORE);
+ result = currentRow.getValue(QueryImpl.JCR_SCORE);
} else if (oakPropertyName.equals(QueryImpl.REP_EXCERPT)) {
- return currentRow.getValue(QueryImpl.REP_EXCERPT);
+ result = currentRow.getValue(QueryImpl.REP_EXCERPT);
+ } else {
+ result = PropertyValues.create(t.getProperty(oakPropertyName));
+ }
+ if (result == null) {
+ return null;
+ }
+ if (propertyType != null && result.getType().tag() != propertyType) {
+ return null;
+ }
+ return result;
+ }
+
+ private void readOakProperties(ArrayList<PropertyValue> target, Tree t, String oakPropertyName, Integer propertyType) {
+ while (true) {
+ if (t == null || !t.exists()) {
+ return;
+ }
+ int slash = oakPropertyName.indexOf('/');
+ if (slash < 0) {
+ break;
+ }
+ String parent = oakPropertyName.substring(0, slash);
+ oakPropertyName = oakPropertyName.substring(slash + 1);
+ if (parent.equals("..")) {
+ t = t.isRoot() ? null : t.getParent();
+ } else if (parent.equals(".")) {
+ // same node
+ } else if (parent.equals("*")) {
+ for (Tree child : t.getChildren()) {
+ readOakProperties(target, child, oakPropertyName, propertyType);
+ }
+ } else {
+ t = t.getChild(parent);
+ }
}
- return PropertyValues.create(t.getProperty(oakPropertyName));
+ if (!"*".equals(oakPropertyName)) {
+ PropertyValue value = currentOakProperty(t, oakPropertyName, propertyType);
+ if (value != null) {
+ target.add(value);
+ }
+ return;
+ }
+ for (PropertyState p : t.getProperties()) {
+ if (propertyType == null || p.getType().tag() == propertyType) {
+ PropertyValue v = PropertyValues.create(p);
+ target.add(v);
+ }
+ }
}
@Override
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java?rev=1562751&r1=1562750&r2=1562751&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SourceImpl.java Thu Jan 30 09:20:02 2014
@@ -176,7 +176,9 @@ public abstract class SourceImpl extends
*
* this creates a filter for the given query
*
+ * @param preparing whether this this the prepare phase
+ * @return a new filter
*/
- abstract public Filter createFilter(boolean preparing);
+ public abstract Filter createFilter(boolean preparing);
}
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=1562751&r1=1562750&r2=1562751&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 Thu Jan 30 09:20:02 2014
@@ -35,6 +35,9 @@ import org.apache.jackrabbit.oak.query.a
import com.google.common.collect.ImmutableSet;
+/**
+ * This class can extract excerpts from node.
+ */
public class SimpleExcerptProvider {
private static final String REP_EXCERPT_FN = "rep:excerpt(.)";
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java?rev=1562751&r1=1562750&r2=1562751&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java Thu Jan 30 09:20:02 2014
@@ -279,7 +279,20 @@ public abstract class AbstractQueryTest
buff.append(", ");
}
PropertyValue v = values[i];
- buff.append(v == null ? "null" : v.getValue(Type.STRING));
+ if (v == null) {
+ buff.append("null");
+ } else if (v.isArray()) {
+ buff.append('[');
+ for (int j = 0; j < v.count(); j++) {
+ buff.append(v.getValue(Type.STRING, j));
+ if (j > 0) {
+ buff.append(", ");
+ }
+ }
+ buff.append(']');
+ } else {
+ buff.append(v.getValue(Type.STRING));
+ }
}
return buff.toString();
}
Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt?rev=1562751&r1=1562750&r2=1562751&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_measure.txt Thu Jan 30 09:20:02 2014
@@ -27,6 +27,20 @@ commit / + "testRoot": { }
commit /testRoot + "parents": { "p0": {"id": "0"}, "p1": {"id": "1"}, "p2": {"id": "2"}}
commit /testRoot + "children": { "c1": {"p": "1"}, "c2": {"p": "1"}, "c3": {"p": "2"}, "c4": {"p": "3"}}
+select [jcr:path] from [nt:base] as p where p.[testRoot/children/*/*] = '3'
+/
+
+select [jcr:path], p.[children/c1/*] from [nt:base] as p where p.[children/c1/*] is not null
+/testRoot, [1]
+
+select [jcr:path], [jcr:score], * from [nt:base] as a where ([id] = '0' or [p0/id] = '0')
+/testRoot/parents, null, null
+/testRoot/parents/p0, null, null
+
+select [jcr:path], [jcr:score], * from [nt:base] as a where ([id] = '0' or [*/id] = '0')
+/testRoot/parents, null, null
+/testRoot/parents/p0, null, null
+
select c.[jcr:path], p.[jcr:path] from [nt:base] as c right outer join [nt:base] as p on p.id = c.p where p.id is not null and isdescendantnode(p, '/testRoot') and isdescendantnode(c, '/testRoot')
/testRoot/children/c1, /testRoot/parents/p1
/testRoot/children/c2, /testRoot/parents/p1
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=1562751&r1=1562750&r2=1562751&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 Jan 30 09:20:02 2014
@@ -23,6 +23,11 @@
# * new tests are typically be added on top, after the syntax docs
# * use ascii character only
+# 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'] */
+
# union (complex)
xpath2sql //*[((@jcr:primaryType = 'nt:unstructured') and (@resources = '/data' or @resolved = '/data'))]
@@ -197,7 +202,10 @@ xpath2sql /jcr:root/testroot/*[jcr:conta
select [jcr:path], [jcr:score], * from [nt:base] as a where contains(*, '"quick brown" -cat') and ischildnode(a, '/testroot') /* xpath: /jcr:root/testroot/*[jcr:contains(., '"quick brown" -cat')] */
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 */
+error: java.lang.IllegalArgumentException: Missing @ in front of the property name: [rep:principalName/*]
+
+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 */
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] */