Author: catholicon
Date: Fri Feb  9 21:31:24 2018
New Revision: 1823707

URL: http://svn.apache.org/viewvc?rev=1823707&view=rev
Log:
OAK-7252: Function index for name() and localname() don't allow sorting

Modified:
    
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-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java

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=1823707&r1=1823706&r2=1823707&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 Feb  9 21:31:24 2018
@@ -143,7 +143,7 @@ public class NodeLocalNameImpl extends D
             return null;
         }
         return new OrderEntry(
-                QueryConstants.RESTRICTION_LOCAL_NAME, 
+                QueryConstants.FUNCTION_RESTRICTION_PREFIX + getFunction(s),
             Type.STRING, 
             o.isDescending() ? 
             OrderEntry.Order.DESCENDING : OrderEntry.Order.ASCENDING);

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=1823707&r1=1823706&r2=1823707&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 Feb  9 21:31:24 2018
@@ -186,7 +186,7 @@ public class NodeNameImpl extends Dynami
             return null;
         }
         return new OrderEntry(
-                QueryConstants.RESTRICTION_NAME, 
+                QueryConstants.FUNCTION_RESTRICTION_PREFIX + getFunction(s),
             Type.STRING, 
             o.isDescending() ? 
             OrderEntry.Order.DESCENDING : OrderEntry.Order.ASCENDING);

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java?rev=1823707&r1=1823706&r2=1823707&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
 Fri Feb  9 21:31:24 2018
@@ -78,6 +78,7 @@ import java.io.InputStream;
 import java.text.ParseException;
 import java.util.Calendar;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -92,6 +93,7 @@ import com.google.common.collect.Compari
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.io.CountingInputStream;
@@ -107,6 +109,7 @@ import org.apache.jackrabbit.oak.api.Res
 import org.apache.jackrabbit.oak.api.ResultRow;
 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.commons.concurrent.ExecutorCloser;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoService;
 import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoServiceImpl;
@@ -875,6 +878,134 @@ public class LucenePropertyIndexTest ext
         assertQuery(query, asList("/node2"));
     }
 
+    @Test
+    public void sortOnNodeName() throws Exception {
+        Tree rootTree = root.getTree("/").addChild("test");
+
+        IndexDefinitionBuilder fnNameFunctionIndex = new 
IndexDefinitionBuilder().noAsync();
+        IndexDefinitionBuilder.PropertyRule rule = 
fnNameFunctionIndex.indexRule("nt:base")
+                .property("nodeName", null)
+                .propertyIndex()
+                .ordered();
+
+        // setup function index on "fn:name()"
+        rule.function("fn:name()");
+        fnNameFunctionIndex.getBuilderTree().setProperty("tags", of("fnName"), 
STRINGS);
+        
fnNameFunctionIndex.build(rootTree.addChild("oak:index").addChild("fnName"));
+
+        // same index as above except for function - "name()"
+        rule.function("name()");
+        fnNameFunctionIndex.getBuilderTree().setProperty("tags", of("name"), 
STRINGS);
+        
fnNameFunctionIndex.build(rootTree.addChild("oak:index").addChild("name"));
+
+        List<String> expected = Lists.newArrayList("/test/oak:index");
+        for (int i = 0; i < 3; i++) {
+            String nodeName = "a" + i;
+            rootTree.addChild(nodeName);
+            expected.add("/test/" + nodeName);
+        }
+        Collections.sort(expected);
+        root.commit();
+
+        String query = "/jcr:root/test/* order by fn:name() option(index tag 
fnName)";
+        assertXpathPlan(query, "lucene:fnName(/test/oak:index/fnName)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:name() ascending option(index 
tag fnName)";
+        assertXpathPlan(query, "lucene:fnName(/test/oak:index/fnName)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:name() descending option(index 
tag fnName)";
+        assertXpathPlan(query, "lucene:fnName(/test/oak:index/fnName)");
+        assertEquals(Lists.reverse(expected), executeQuery(query, XPATH));
+
+        // order by fn:name() although function index is on "name()"
+        query = "/jcr:root/test/* order by fn:name() option(index tag name)";
+        assertXpathPlan(query, "lucene:name(/test/oak:index/name)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:name() ascending option(index 
tag name)";
+        assertXpathPlan(query, "lucene:name(/test/oak:index/name)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:name() descending option(index 
tag name)";
+        assertXpathPlan(query, "lucene:name(/test/oak:index/name)");
+        assertEquals(Lists.reverse(expected), executeQuery(query, XPATH));
+    }
+
+    @Test
+    public void sortOnLocalName() throws Exception {
+        Tree rootTree = root.getTree("/").addChild("test");
+
+        IndexDefinitionBuilder fnNameFunctionIndex = new 
IndexDefinitionBuilder().noAsync();
+        IndexDefinitionBuilder.PropertyRule rule = 
fnNameFunctionIndex.indexRule("nt:base")
+                .property("nodeName", null)
+                .propertyIndex()
+                .ordered();
+
+        // setup function index on "fn:name()"
+        rule.function("fn:local-name()");
+        fnNameFunctionIndex.getBuilderTree().setProperty("tags", 
of("fnLocalName"), STRINGS);
+        
fnNameFunctionIndex.build(rootTree.addChild("oak:index").addChild("fnLocalName"));
+
+        // same index as above except for function - "name()"
+        rule.function("localname()");
+        fnNameFunctionIndex.getBuilderTree().setProperty("tags", 
of("localName"), STRINGS);
+        
fnNameFunctionIndex.build(rootTree.addChild("oak:index").addChild("localName"));
+
+        List<String> expected = Lists.newArrayList("/test/oak:index");
+        for (int i = 0; i < 3; i++) {
+            String nodeName = "ja" + i;//'j*' should come after (asc) 'index' 
in sort order
+            rootTree.addChild(nodeName);
+            expected.add("/test/" + nodeName);
+        }
+
+        //sort expectation based on local name
+        Collections.sort(expected, (s1, s2) -> {
+            final StringBuffer sb1 = new StringBuffer();
+            PathUtils.elements(s1).forEach(elem -> {
+                String[] split = elem.split(":", 2);
+                sb1.append(split[split.length - 1]);
+            });
+            s1 = sb1.toString();
+
+            final StringBuffer sb2 = new StringBuffer();
+            PathUtils.elements(s2).forEach(elem -> {
+                String[] split = elem.split(":", 2);
+                sb2.append(split[split.length - 1]);
+            });
+            s2 = sb2.toString();
+
+            return s1.compareTo(s2);
+        });
+        root.commit();
+
+        String query = "/jcr:root/test/* order by fn:local-name() option(index 
tag fnLocalName)";
+        assertXpathPlan(query, 
"lucene:fnLocalName(/test/oak:index/fnLocalName)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:local-name() ascending 
option(index tag fnLocalName)";
+        assertXpathPlan(query, 
"lucene:fnLocalName(/test/oak:index/fnLocalName)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:local-name() descending 
option(index tag fnLocalName)";
+        assertXpathPlan(query, 
"lucene:fnLocalName(/test/oak:index/fnLocalName)");
+        assertEquals(Lists.reverse(expected), executeQuery(query, XPATH));
+
+        // order by fn:name() although function index is on "name()"
+        query = "/jcr:root/test/* order by fn:local-name() option(index tag 
localName)";
+        assertXpathPlan(query, "lucene:localName(/test/oak:index/localName)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:local-name() ascending 
option(index tag localName)";
+        assertXpathPlan(query, "lucene:localName(/test/oak:index/localName)");
+        assertEquals(expected, executeQuery(query, XPATH));
+
+        query = "/jcr:root/test/* order by fn:local-name() descending 
option(index tag localName)";
+        assertXpathPlan(query, "lucene:localName(/test/oak:index/localName)");
+        assertEquals(Lists.reverse(expected), executeQuery(query, XPATH));
+    }
+
     //OAK-4517
     @Test
     public void pathIncludeSubrootIndex() throws Exception {
@@ -2747,9 +2878,12 @@ public class LucenePropertyIndexTest ext
                 "lucene:test1(/oak:index/test1)", asList("/d"));
     }
 
-    private void assertPlanAndQuery(String query, String planExpectation, 
List<String> paths){
-        assertThat(explain(query), containsString(planExpectation));
-        assertQuery(query, paths);
+    private void assertPlanAndQuery(String query, String planExpectation, 
List<String> paths) {
+        assertPlanAndQuery(query, planExpectation, paths, false);
+    }
+    private void assertPlanAndQuery(String query, String planExpectation, 
List<String> paths, boolean ordered){
+        assertPlan(query, planExpectation);
+        assertQuery(query, SQL2, paths, ordered);
     }
 
     private static Tree createNodeWithMixinType(Tree t, String nodeName, 
String typeName){
@@ -2794,6 +2928,14 @@ public class LucenePropertyIndexTest ext
 
     //TODO Test for range with Date. Check for precision
 
+    private void assertPlan(String query, String planExpectation) {
+        assertThat(explain(query), containsString(planExpectation));
+    }
+
+    private void assertXpathPlan(String query, String planExpectation) throws 
ParseException {
+        assertThat(explainXpath(query), containsString(planExpectation));
+    }
+
     private String explain(String query){
         String explain = "explain " + query;
         return executeQuery(explain, "JCR-SQL2").get(0);


Reply via email to