Author: thomasm
Date: Fri Jun 10 12:49:32 2016
New Revision: 1747703

URL: http://svn.apache.org/viewvc?rev=1747703&view=rev
Log:
OAK-3574 Query engine: support p=lowercase(x) and other function-based indexes 
(WIP)

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.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/UpperCaseImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryConstants.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
 Fri Jun 10 12:49:32 2016
@@ -29,9 +29,33 @@ public abstract class DynamicOperandImpl
 
     public abstract PropertyValue currentProperty();
 
+    /**
+     * Apply a restriction of type "this = value" to the given filter.
+     * 
+     * @param f the filter where the restriction is applied.
+     * @param operator the operator (for example "=").
+     * @param v the value
+     */
     public abstract void restrict(FilterImpl f, Operator operator, 
PropertyValue v);
-    
+
+    /**
+     * Apply a restriction of type "this in (list)" to the given filter.
+     * 
+     * @param f the filter where the restriction is applied.
+     * @param list the list of values
+     */
     public abstract void restrictList(FilterImpl f, List<PropertyValue> list);
+    
+    /**
+     * Apply a restriction of type "function(this) = value" to the given 
filter.
+     * 
+     * @param functionName the function name (for example "upper")
+     * @param f the filter where the restriction is applied.
+     * @param operator the operator (for example "=").
+     * @param v the value
+     */
+    public abstract void restrictFunction(FilterImpl f, String functionName, 
Operator operator,
+            PropertyValue v);
 
     /**
      * Check whether the condition can be applied to a selector (to restrict 
the

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
 Fri Jun 10 12:49:32 2016
@@ -92,6 +92,11 @@ public class FullTextSearchScoreImpl ext
     }
 
     @Override
+    public void restrictFunction(FilterImpl f, String functionName, Operator 
operator, PropertyValue v) {
+        // optimizations of the type "upper(jcr:score()) = '1'" are not 
supported
+    }
+
+    @Override
     public boolean canRestrictSelector(SelectorImpl s) {
         return s.equals(selector);
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
 Fri Jun 10 12:49:32 2016
@@ -100,6 +100,7 @@ public class LengthImpl extends DynamicO
         }
         // LENGTH(x) implies x is not null
         propertyValue.restrict(f, Operator.NOT_EQUAL, null);
+        propertyValue.restrictFunction(f, "length", operator, v);
     }
     
     @Override
@@ -108,6 +109,11 @@ public class LengthImpl extends DynamicO
     }
 
     @Override
+    public void restrictFunction(FilterImpl f, String functionName, Operator 
operator, PropertyValue v) {
+        // optimizations of the type "upper(length(x)) = '1'" are not supported
+    }
+
+    @Override
     public boolean canRestrictSelector(SelectorImpl s) {
         return propertyValue.canRestrictSelector(s);
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
 Fri Jun 10 12:49:32 2016
@@ -94,6 +94,7 @@ public class LowerCaseImpl extends Dynam
     public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         // LOWER(x) implies x is not null
         operand.restrict(f, Operator.NOT_EQUAL, null);
+        operand.restrictFunction(f, "lower", operator, v);
     }
     
     @Override
@@ -103,6 +104,11 @@ public class LowerCaseImpl extends Dynam
     }
 
     @Override
+    public void restrictFunction(FilterImpl f, String functionName, Operator 
operator, PropertyValue v) {
+        // optimizations of the type "lower(upper(x)) = 'x'" are not currently 
supported
+    }
+
+    @Override
     public boolean canRestrictSelector(SelectorImpl s) {
         return operand.canRestrictSelector(s);
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NativeFunctionImpl.java
 Fri Jun 10 12:49:32 2016
@@ -35,8 +35,11 @@ public class NativeFunctionImpl extends
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
+    /**
+     * The prefix for native function restrictions (native, similar, 
spellcheck, suggest).
+     */
     public static final String NATIVE_PREFIX = "native*";
-    
+
     private final String selectorName;
     private final String language;
     private final StaticOperandImpl nativeSearchExpression;

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
 Fri Jun 10 12:49:32 2016
@@ -85,7 +85,6 @@ public class NodeLocalNameImpl extends D
         if (v == null) {
             return;
         }
-
         String name = NodeNameImpl.getName(query, v);
         if (name != null && f.getSelector().equals(selector)
                 && NodeNameImpl.supportedOperator(operator)) {
@@ -98,6 +97,22 @@ public class NodeLocalNameImpl extends D
     public void restrictList(FilterImpl f, List<PropertyValue> list) {
         // optimizations of type "LOCALNAME(..) IN(A, B)" are not supported
     }
+    
+    @Override
+    public void restrictFunction(FilterImpl f, String functionName, Operator 
operator, PropertyValue v) {
+        // optimizations of the type "lower(LOCALNAME(x)) = 'x'"
+        if (v == null) {
+            return;
+        }
+        String name = NodeNameImpl.getName(query, v);
+        if (name != null && f.getSelector().equals(selector)
+                && NodeNameImpl.supportedOperator(operator)) {
+            String restrictionName = 
QueryConstants.FUNCTION_RESTRICTION_PREFIX + 
+                    functionName + "*@" + 
QueryConstants.RESTRICTION_LOCAL_NAME;            
+            f.restrictProperty(restrictionName,
+                    operator, PropertyValues.newString(name));
+        }
+    }
 
     @Override
     public boolean supportsRangeConditions() {

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
 Fri Jun 10 12:49:32 2016
@@ -103,6 +103,22 @@ public class NodeNameImpl extends Dynami
     }
 
     @Override
+    public void restrictFunction(FilterImpl f, String functionName, Operator 
operator, PropertyValue v) {
+        if (v == null) {
+            return;
+        }
+        String name = getName(query, v);
+        if (name != null && f.getSelector().equals(selector)
+                && NodeNameImpl.supportedOperator(operator)) {
+            String localName = NodeLocalNameImpl.getLocalName(name);
+            String restrictionName = 
QueryConstants.FUNCTION_RESTRICTION_PREFIX + 
+                    functionName + "*@" + 
QueryConstants.RESTRICTION_LOCAL_NAME;            
+            f.restrictProperty(restrictionName,
+                    operator, PropertyValues.newString(localName));
+        }
+    }
+
+    @Override
     public boolean canRestrictSelector(SelectorImpl s) {
         return s.equals(selector);
     }

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=1747703&r1=1747702&r2=1747703&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
 Fri Jun 10 12:49:32 2016
@@ -31,6 +31,7 @@ import org.apache.jackrabbit.oak.query.Q
 import org.apache.jackrabbit.oak.query.SQL2Parser;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.spi.query.Filter.PathRestriction;
+import org.apache.jackrabbit.oak.spi.query.QueryConstants;
 
 /**
  * A property expression.
@@ -140,6 +141,16 @@ public class PropertyValueImpl extends D
             f.restrictPropertyAsList(pn, list);
         }
     }
+    
+    @Override
+    public void restrictFunction(FilterImpl f, String functionName, Operator 
operator, PropertyValue v) {
+        if (f.getSelector().equals(selector)) {
+            String pn = normalizePropertyName(propertyName);
+            String restrictionName = 
QueryConstants.FUNCTION_RESTRICTION_PREFIX +
+                    functionName + "*@" + pn;
+            f.restrictProperty(restrictionName, operator, v, propertyType);
+        }
+    }
 
     @Override
     public boolean canRestrictSelector(SelectorImpl s) {

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java
 Fri Jun 10 12:49:32 2016
@@ -80,6 +80,7 @@ public class UpperCaseImpl extends Dynam
     public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         // UPPER(x) implies x is not null
         operand.restrict(f, Operator.NOT_EQUAL, null);
+        operand.restrictFunction(f, "upper", operator, v);
     }
     
     @Override
@@ -87,6 +88,11 @@ public class UpperCaseImpl extends Dynam
         // "UPPER(x) IN (A, B)" implies x is not null
         operand.restrict(f, Operator.NOT_EQUAL, null);
     }
+    
+    @Override
+    public void restrictFunction(FilterImpl f, String functionName, Operator 
operator, PropertyValue v) {
+        // optimizations of the type "lower(upper(x)) = 'x'" are not supported
+    }    
 
     @Override
     public boolean canRestrictSelector(SelectorImpl s) {

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryConstants.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryConstants.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryConstants.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryConstants.java
 Fri Jun 10 12:49:32 2016
@@ -26,5 +26,22 @@ public abstract class QueryConstants {
      * via NAME and LOCALNAME functions
      */
     public static final String RESTRICTION_LOCAL_NAME = ":localname";
+    
+    /**
+     * The prefix for restrictions for function-based indexes, for example
+     * upper(propertyName). Syntax: "function*expression". In order to support
+     * all kinds of expressions in the future (including nested expressions and
+     * so on), the format for the expression is written in the Polish notation
+     * (the RPN, reversed), with "*" as delimiter (as property names may not
+     * contain "*"), and "@" in front of each property name to distinguish
+     * between property names and functions. Literals are quoted. Examples: The
+     * expression "lower(lastName)" is converted to "function*lower {@literal 
@}
+     * lastName". The expression "lower(lastName)" is converted to
+     * "lower(upper(lastName))" is converted to "function*lower*upper*
+     * {@literal @}lastName". The condition
+     * "firstName+' '+lastName = 'Tim Cook'" would be "function*+*+ {@literal 
@}
+     * firstName*' ' {@literal @}lastName.
+     */
+    public static final String FUNCTION_RESTRICTION_PREFIX = "function*";
 
 }
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/FilterTest.java
 Fri Jun 10 12:49:32 2016
@@ -52,6 +52,49 @@ public class FilterTest {
     }
     
     @Test
+    public void functionBasedIndex() throws Exception {
+        String sql2 = "select [jcr:path] from [nt:base] where lower([test]) = 
'hello'";
+        assertEquals("Filter(query=select [jcr:path] from [nt:base] " + 
+                "where lower([test]) = 'hello', " + 
+                "path=*, property=[" + 
+                "function*lower*@test=[hello], " +
+                "test=[is not null]])", createFilterSQL(sql2).toString());
+        
+        sql2 = "select [jcr:path] from [nt:base] where upper([test]) = 
'HELLO'";
+        assertEquals("Filter(query=select [jcr:path] from [nt:base] " + 
+                "where upper([test]) = 'HELLO', " + 
+                "path=*, property=[" + 
+                "function*upper*@test=[HELLO], " +
+                "test=[is not null]])", createFilterSQL(sql2).toString());
+        
+        sql2 = "select [jcr:path] from [nt:base] where upper(name()) = 
'ACME:TEST'";
+        assertEquals("Filter(query=select [jcr:path] from [nt:base] " + 
+                "where upper(name()) = 'ACME:TEST', " + 
+                "path=*, property=[" + 
+                "function*upper*@:localname=[TEST]])", 
createFilterSQL(sql2).toString());
+        
+        sql2 = "select [jcr:path] from [nt:base] where lower(localname()) = 
'test'";
+        assertEquals("Filter(query=select [jcr:path] from [nt:base] " + 
+                "where lower(localname()) = 'test', " + 
+                "path=*, property=[" + 
+                "function*lower*@:localname=[test]])", 
createFilterSQL(sql2).toString());
+
+        sql2 = "select [jcr:path] from [nt:base] where length([test]) <= 10";
+        assertEquals("Filter(query=select [jcr:path] from [nt:base] " + 
+                "where length([test]) <= 10, " + 
+                "path=*, property=[test=[is not null], " + 
+                "function*length*@test=[..10]]])", 
createFilterSQL(sql2).toString());
+        
+        sql2 = "select [jcr:path] from [nt:base] where length([test]) > 2";
+        assertEquals("Filter(query=select [jcr:path] from [nt:base] " + 
+                "where length([test]) > 2, " + 
+                "path=*, property=[test=[is not null], " + 
+                "function*length*@test=[(2..]])", 
createFilterSQL(sql2).toString());
+        
+
+    }
+    
+    @Test
     public void oak4170() throws ParseException {
         String sql2 = "select * from [nt:unstructured] where 
CONTAINS([jcr:content/metadata/comment], 'december')";
         Filter f = createFilterSQL(sql2);

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java?rev=1747703&r1=1747702&r2=1747703&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 Fri Jun 10 12:49:32 2016
@@ -154,6 +154,10 @@ class IndexPlanner {
                 if (QueryConstants.RESTRICTION_LOCAL_NAME.equals(name)) {
                     continue;
                 }
+                if 
(name.startsWith(QueryConstants.FUNCTION_RESTRICTION_PREFIX)) {
+                    // TODO support function-based indexes
+                    continue;
+                }
                 if (QueryImpl.REP_FACET.equals(pr.propertyName)) {
                     String value = pr.first.getValue(Type.STRING);
                     facetFields.add(FacetHelper.parseFacetField(value));


Reply via email to