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] */