Author: reschke
Date: Fri Jan 31 18:21:15 2020
New Revision: 1873427
URL: http://svn.apache.org/viewvc?rev=1873427&view=rev
Log:
OAK-8845 | Function-based indexing don't support ordering (merged r1873010 and
r1873070 into 1.22)
Modified:
jackrabbit/oak/branches/1.22/ (props changed)
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
jackrabbit/oak/branches/1.22/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FunctionIndexTest.java
jackrabbit/oak/branches/1.22/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
jackrabbit/oak/branches/1.22/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
Propchange: jackrabbit/oak/branches/1.22/
------------------------------------------------------------------------------
Merged /jackrabbit/oak/trunk:r1873010,1873070
Modified:
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
Fri Jan 31 18:21:15 2020
@@ -625,19 +625,16 @@ public class QueryImpl implements Query
OrderEntry e = list.get(i);
OrderingImpl o = orderings[i];
DynamicOperandImpl op = o.getOperand();
- if (!(op instanceof PropertyValueImpl)) {
- // ordered by a function: currently not supported
- canSortByIndex = false;
- break;
- }
// we only have one selector, so no need to check that
// TODO support joins
- String pn = ((PropertyValueImpl) op).getPropertyName();
- if (!pn.equals(e.getPropertyName())) {
+ String pn =
op.getOrderEntryPropertyName(selectors.get(0));
+
+ if (pn == null || !pn.equals(e.getPropertyName())) {
// ordered by another property
canSortByIndex = false;
break;
}
+
if (o.isDescending() != (e.getOrder() ==
Order.DESCENDING)) {
// ordered ascending versus descending
canSortByIndex = false;
Modified:
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
Fri Jan 31 18:21:15 2020
@@ -21,6 +21,7 @@ import java.util.Set;
import org.apache.jackrabbit.oak.api.PropertyValue;
import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.QueryConstants;
import org.apache.jackrabbit.oak.spi.query.QueryIndex.OrderEntry;
/**
@@ -114,4 +115,17 @@ public abstract class DynamicOperandImpl
*/
public abstract OrderEntry getOrderEntry(SelectorImpl s, OrderingImpl o);
+ /**
+ *
+ * @param s
+ * @return the property name as defined in the OrderEntry for the
DynamicOperand
+ */
+ public String getOrderEntryPropertyName(SelectorImpl s) {
+ String fn = getFunction(s);
+ if (fn != null) {
+ return QueryConstants.FUNCTION_RESTRICTION_PREFIX + fn;
+ }
+ return null;
+ }
+
}
Modified:
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
Fri Jan 31 18:21:15 2020
@@ -118,4 +118,9 @@ public class FullTextSearchScoreImpl ext
return null;
}
+ @Override
+ public String getOrderEntryPropertyName(SelectorImpl s) {
+ return null;
+ }
+
}
Modified:
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
Fri Jan 31 18:21:15 2020
@@ -149,6 +149,7 @@ public class LowerCaseImpl extends Dynam
OrderEntry.Order.DESCENDING : OrderEntry.Order.ASCENDING);
}
return null;
- }
+ }
+
}
Modified:
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
Fri Jan 31 18:21:15 2020
@@ -59,6 +59,7 @@ public class PropertyValueImpl extends D
return selectorName;
}
+
public String getPropertyName() {
return propertyName;
}
@@ -180,4 +181,13 @@ public class PropertyValueImpl extends D
OrderEntry.Order.DESCENDING : OrderEntry.Order.ASCENDING);
}
+ @Override
+ public String getOrderEntryPropertyName(SelectorImpl s) {
+ if (!s.equals(selector)) {
+ // ordered by a different selector
+ return null;
+ }
+ return normalizePropertyName(propertyName);
+ }
+
}
Modified:
jackrabbit/oak/branches/1.22/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FunctionIndexTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FunctionIndexTest.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FunctionIndexTest.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FunctionIndexTest.java
Fri Jan 31 18:21:15 2020
@@ -24,6 +24,7 @@ import static org.apache.jackrabbit.oak.
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.useV2;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -241,6 +242,632 @@ public class FunctionIndexTest extends A
}
@Test
+ public void testOrdering2() throws Exception {
+ Tree index = root.getTree("/");
+ Tree indexDefn = createTestIndexNode(index,
LuceneIndexConstants.TYPE_LUCENE);
+ useV2(indexDefn);
+ indexDefn.setProperty(LuceneIndexConstants.TEST_MODE, true);
+
indexDefn.setProperty(FulltextIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:unstructured");
+ props.getParent().setProperty(FulltextIndexConstants.INDEX_NODE_NAME,
true);
+ TestUtil.enableForFullText(props,
FulltextIndexConstants.REGEX_ALL_PROPS, true);
+ Tree upper = TestUtil.enableFunctionIndex(props, "upper([foo])");
+ upper.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ root.commit();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+
+ Tree a = test.addChild("n1");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "hello");
+
+ a = test.addChild("n2");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "World!");
+ a = test.addChild("n3");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "Hallo");
+ a = test.addChild("n4");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "10%");
+ a = test.addChild("n5");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "10 percent");
+
+ a = test.addChild("n0");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a = test.addChild("n9");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+ String query = "select a.[foo]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo)";
+
+ root.commit();
+
+ assertThat(explain(query),
containsString("lucene:test-index(/oak:index/test-index)"));
+
+ List<String> result = executeQuery(query, SQL2);
+ assertEquals("Ordering doesn't match", asList("10 percent", "10%",
"Hallo", "hello", "World!"), result);
+
+ }
+
+
+ /*
+ Test order by func(a),func(b)
+ order by func(b),func(a)
+ func(a) DESC,func(b)
+ func(a),func(b)DESC
+ where both func(a) and func(b) have ordered set = true
+ Correct ordering is effectively served by the index
+ */
+ @Test
+ public void testOrdering3() throws Exception {
+
+ Tree index = root.getTree("/");
+ Tree indexDefn = createTestIndexNode(index,
LuceneIndexConstants.TYPE_LUCENE);
+ useV2(indexDefn);
+ indexDefn.setProperty(LuceneIndexConstants.TEST_MODE, true);
+
indexDefn.setProperty(FulltextIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:unstructured");
+ props.getParent().setProperty(FulltextIndexConstants.INDEX_NODE_NAME,
true);
+ TestUtil.enableForFullText(props,
FulltextIndexConstants.REGEX_ALL_PROPS, true);
+
+ Tree upper = TestUtil.enableFunctionIndex(props, "upper([foo])");
+ upper.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ Tree upper2 = TestUtil.enableFunctionIndex(props, "upper([foo2])");
+ upper2.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+
+ root.commit();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+
+ Tree a = test.addChild("n1");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b2");
+
+ a = test.addChild("n2");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a2");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n3");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a3");
+ a.setProperty("foo2", "b1");
+
+ a = test.addChild("n4");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n5");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b1");
+
+
+ String query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo),upper(a.foo2)";
+
+ root.commit();
+
+ List<String> result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a1, b1", "a1, b2", "a1,
b3", "a2, b3", "a3, b1"), result);
+
+ query = "select a.[foo2],a.[foo]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo2),upper(a.foo)";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("b1, a1", "b1, a3", "b2,
a1", "b3, a1", "b3, a2"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo) DESC, upper(a.foo2)";
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a3, b1", "a2, b3", "a1,
b1", "a1, b2", "a1, b3"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo), upper(a.foo2) DESC";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a1, b3", "a1, b2", "a1,
b1", "a2, b3", "a3, b1"), result);
+
+
+ }
+
+ /*
+ Test order by func(a),func(b)
+ order by func(b),func(a)
+ func(a) DESC,func(b)
+ func(a),func(b)DESC
+ where only func(a) is ordered by index
+ The effective ordering in this case will be done by QueryEngine
+ */
+ @Test
+ public void testOrdering4() throws Exception {
+ Tree index = root.getTree("/");
+ Tree indexDefn = createTestIndexNode(index,
LuceneIndexConstants.TYPE_LUCENE);
+ useV2(indexDefn);
+ indexDefn.setProperty(LuceneIndexConstants.TEST_MODE, true);
+
indexDefn.setProperty(FulltextIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:unstructured");
+ props.getParent().setProperty(FulltextIndexConstants.INDEX_NODE_NAME,
true);
+ TestUtil.enableForFullText(props,
FulltextIndexConstants.REGEX_ALL_PROPS, true);
+
+ Tree upper = TestUtil.enableFunctionIndex(props, "upper([foo])");
+ upper.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ TestUtil.enableFunctionIndex(props, "upper([foo2])");
+
+
+ root.commit();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+
+ Tree a = test.addChild("n1");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b2");
+
+ a = test.addChild("n2");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a2");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n3");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a3");
+ a.setProperty("foo2", "b1");
+
+ a = test.addChild("n4");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n5");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b1");
+
+
+ String query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo),upper(a.foo2)";
+
+ root.commit();
+
+ List<String> result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a1, b1", "a1, b2", "a1,
b3", "a2, b3", "a3, b1"), result);
+
+ query = "select a.[foo2],a.[foo]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo2),upper(a.foo)";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("b1, a1", "b1, a3", "b2,
a1", "b3, a1", "b3, a2"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo) DESC, upper(a.foo2)";
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a3, b1", "a2, b3", "a1,
b1", "a1, b2", "a1, b3"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo), upper(a.foo2) DESC";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a1, b3", "a1, b2", "a1,
b1", "a2, b3", "a3, b1"), result);
+
+ }
+
+ /*
+ Test order by func(a),b
+ order by b,func(a)
+ order by func(a) DESC,b
+ order by func(a),b DESC
+ where both b and func(a) have ordered=true
+ */
+ @Test
+ public void testOrdering5() throws Exception {
+ Tree index = root.getTree("/");
+ Tree indexDefn = createTestIndexNode(index,
LuceneIndexConstants.TYPE_LUCENE);
+ useV2(indexDefn);
+ indexDefn.setProperty(LuceneIndexConstants.TEST_MODE, true);
+
indexDefn.setProperty(FulltextIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:unstructured");
+ props.getParent().setProperty(FulltextIndexConstants.INDEX_NODE_NAME,
true);
+ TestUtil.enableForFullText(props,
FulltextIndexConstants.REGEX_ALL_PROPS, true);
+
+ Tree upper = TestUtil.enableFunctionIndex(props, "upper([foo])");
+ upper.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+
+ Tree upper2 = TestUtil.enablePropertyIndex(props, "foo2", false);
+ upper2.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+
+ root.commit();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+
+ Tree a = test.addChild("n1");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b2");
+
+ a = test.addChild("n2");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a2");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n3");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a3");
+ a.setProperty("foo2", "b1");
+
+ a = test.addChild("n4");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n5");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b1");
+
+
+ String query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo),a.foo2";
+
+ root.commit();
+
+ List<String> result = executeQuery(query, SQL2);
+
+
+ assertEquals("Ordering doesn't match", asList("a1, b1", "a1, b2", "a1,
b3", "a2, b3", "a3, b1"), result);
+
+ query = "select a.[foo2],a.[foo]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by a.foo2,upper(a.foo)";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("b1, a1", "b1, a3", "b2,
a1", "b3, a1", "b3, a2"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo) DESC, a.foo2";
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a3, b1", "a2, b3", "a1,
b1", "a1, b2", "a1, b3"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo), a.foo2 DESC";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a1, b3", "a1, b2", "a1,
b1", "a2, b3", "a3, b1"), result);
+
+ }
+
+ /*
+ Test order by func(a),b
+ orrder by b,func(a)
+ order by func(a) DESC,b
+ order by func(a),b DESC
+ where func(a) does not have ordered = true
+ */
+ @Test
+ public void testOrdering6() throws Exception {
+ Tree index = root.getTree("/");
+ Tree indexDefn = createTestIndexNode(index,
LuceneIndexConstants.TYPE_LUCENE);
+ useV2(indexDefn);
+ indexDefn.setProperty(LuceneIndexConstants.TEST_MODE, true);
+
indexDefn.setProperty(FulltextIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:unstructured");
+ props.getParent().setProperty(FulltextIndexConstants.INDEX_NODE_NAME,
true);
+ TestUtil.enableForFullText(props,
FulltextIndexConstants.REGEX_ALL_PROPS, true);
+
+ TestUtil.enableFunctionIndex(props, "upper([foo])");
+
+
+ Tree upper2 = TestUtil.enablePropertyIndex(props, "foo2", false);
+ upper2.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+
+ root.commit();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+
+ Tree a = test.addChild("n1");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b2");
+
+ a = test.addChild("n2");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a2");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n3");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a3");
+ a.setProperty("foo2", "b1");
+
+ a = test.addChild("n4");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b3");
+
+ a = test.addChild("n5");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "a1");
+ a.setProperty("foo2", "b1");
+
+
+ String query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo),a.foo2";
+
+ root.commit();
+
+ List<String> result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a1, b1", "a1, b2", "a1,
b3", "a2, b3", "a3, b1"), result);
+
+ query = "select a.[foo2],a.[foo]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by a.foo2,upper(a.foo)";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("b1, a1", "b1, a3", "b2,
a1", "b3, a1", "b3, a2"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo) DESC, a.foo2";
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a3, b1", "a2, b3", "a1,
b1", "a1, b2", "a1, b3"), result);
+
+ query = "select a.[foo],a.[foo2]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.foo is not null and isdescendantnode(a , '/test')
order by upper(a.foo), a.foo2 DESC";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("a1, b3", "a1, b2", "a1,
b1", "a2, b3", "a3, b1"), result);
+ }
+
+ /*
+ Testing order by for
+ different function implementations
+ */
+ @Test
+ public void testOrdering7() throws Exception {
+ Tree index = root.getTree("/");
+ Tree indexDefn = createTestIndexNode(index,
LuceneIndexConstants.TYPE_LUCENE);
+ useV2(indexDefn);
+ indexDefn.setProperty(LuceneIndexConstants.TEST_MODE, true);
+
indexDefn.setProperty(FulltextIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:unstructured");
+ props.getParent().setProperty(FulltextIndexConstants.INDEX_NODE_NAME,
true);
+ TestUtil.enableForFullText(props,
FulltextIndexConstants.REGEX_ALL_PROPS, true);
+
+ Tree fn = TestUtil.enableFunctionIndex(props, "upper([foo])");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ fn = TestUtil.enableFunctionIndex(props, "lower([foo])");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ fn = TestUtil.enableFunctionIndex(props, "length([foo])");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+ // Any function property trying to sory by length needs to explicitly
set the type to Long
+ fn.setProperty(FulltextIndexConstants.PROP_TYPE, "Long");
+
+ fn = TestUtil.enableFunctionIndex(props, "coalesce([foo2],[foo])");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ fn = TestUtil.enableFunctionIndex(props, "name()");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ fn = TestUtil.enableFunctionIndex(props, "localname()");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ fn = TestUtil.enableFunctionIndex(props, "lower(coalesce([foo2],
coalesce([foo], localname())))");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+
+ fn = TestUtil.enableFunctionIndex(props, "length(coalesce([foo],
coalesce([foo2], localname())))");
+ fn.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+ fn.setProperty(FulltextIndexConstants.PROP_TYPE, "Long");
+
+ root.commit();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+
+ Tree a = test.addChild("d1");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "c");
+
+ a = test.addChild("d2");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "bbbb");
+ a.setProperty("foo2", "22");
+
+
+ a = test.addChild("d3");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "aa");
+
+ a = test.addChild("jcr:content");
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "test");
+ a.setProperty("foo2", "11");
+
+ root.commit();
+
+ String query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where isdescendantnode(a , '/test') order by
coalesce([foo2],[foo]) ";
+
+ List<String> result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/jcr:content",
"/test/d2", "/test/d3", "/test/d1"), result);
+
+ query = "select a.[foo]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where isdescendantnode(a , '/test') order by
lower([a].[foo])";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("aa", "bbbb", "c",
"test"), result);
+
+ query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where isdescendantnode(a , '/test') order by localname()
";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/jcr:content",
"/test/d1", "/test/d2", "/test/d3"), result);
+
+
+ query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where isdescendantnode(a , '/test') order by name() ";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/d1", "/test/d2",
"/test/d3", "/test/jcr:content"), result);
+
+ query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where isdescendantnode(a , '/test') order by
lower(coalesce([a].[foo2], coalesce([a].[foo], localname())))";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/jcr:content",
"/test/d2", "/test/d3", "/test/d1"), result);
+
+ query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where isdescendantnode(a , '/test') order by
lower(coalesce([a].[foo2], coalesce([a].[foo], localname()))) DESC";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/d1", "/test/d3",
"/test/d2", "/test/jcr:content"), result);
+
+ query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.[foo] is not null AND isdescendantnode(a ,
'/test') order by length([a].[foo]) DESC, localname()";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/jcr:content",
"/test/d2", "/test/d3", "/test/d1"), result);
+
+ query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.[foo] is not null AND isdescendantnode(a ,
'/test') order by length([a].[foo]), localname()";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/d1", "/test/d3",
"/test/jcr:content", "/test/d2"), result);
+
+
+ query = "select [jcr:path]\n" +
+ "\t from [nt:unstructured] as a\n" +
+ "\t where a.[foo] is not null AND isdescendantnode(a ,
'/test') order by length(coalesce([foo], coalesce([foo2], localname()))),
localname() DESC";
+
+ result = executeQuery(query, SQL2);
+
+ assertEquals("Ordering doesn't match", asList("/test/d1", "/test/d3",
"/test/d2", "/test/jcr:content"), result);
+
+
+ }
+
+ @Test
+ public void testOrdering() throws Exception {
+ Tree luceneIndex = createIndex("upper",
Collections.<String>emptySet());
+ Tree nonFunc = luceneIndex.addChild(FulltextIndexConstants.INDEX_RULES)
+ .addChild("nt:base")
+ .addChild(FulltextIndexConstants.PROP_NODE)
+ .addChild("foo");
+ nonFunc.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+ nonFunc.setProperty(FulltextIndexConstants.PROP_PROPERTY_INDEX, true);
+ nonFunc.setProperty("name", "foo");
+
+ Tree func = luceneIndex.getChild(FulltextIndexConstants.INDEX_RULES)
+ .getChild("nt:base")
+ .getChild(FulltextIndexConstants.PROP_NODE)
+ .addChild("fooUpper");
+ func.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+ func.setProperty(FulltextIndexConstants.PROP_FUNCTION,
"fn:upper-case(@foo)");
+ func.setProperty(FulltextIndexConstants.PROP_PROPERTY_INDEX, true);
+
+ root.commit();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+ List<String> paths = Lists.newArrayList();
+ for (int idx = 0; idx < 10; idx++) {
+ paths.add("/test/n" + idx);
+ if (idx % 2 == 0) continue;
+ Tree a = test.addChild("n" + idx);
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "bar" + idx);
+
+ }
+ for (int idx = 0; idx < 10; idx++) {
+ if (idx % 2 != 0) continue;
+ Tree a = test.addChild("n" + idx);
+ a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+ a.setProperty("foo", "bar" + idx);
+ }
+ root.commit();
+
+ String query = "/jcr:root//element(*, nt:unstructured)
[jcr:like(fn:upper-case(@foo),'BAR%')] order by foo";
+ assertThat(explainXpath(query), containsString("lucene:upper"));
+ List<String> result = assertQuery(query, "xpath", paths);
+ assertEquals("Ordering doesn't match", paths, result);
+
+
+ query = "/jcr:root//element(*, nt:unstructured)
[jcr:like(fn:upper-case(@foo),'BAR%')] order by fn:upper-case(@foo)";
+ assertThat(explainXpath(query), containsString("lucene:upper"));
+ List<String> result2 = assertQuery(query, "xpath", paths);
+ assertEquals("Ordering doesn't match", paths, result2);
+ }
+
+ @Test
public void upperCaseRelative() throws Exception {
Tree luceneIndex = createIndex("upper",
Collections.<String>emptySet());
Tree func = luceneIndex.addChild(FulltextIndexConstants.INDEX_RULES)
@@ -393,6 +1020,7 @@ public class FunctionIndexTest extends A
}
}
+
/*
Given an index def with 2 orderable property definitions(Relative) for
same property - one with function and one without
Order by should give correct results
Modified:
jackrabbit/oak/branches/1.22/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
Fri Jan 31 18:21:15 2020
@@ -884,7 +884,7 @@ public class FulltextIndexPlanner {
orderEntries.add(IndexDefinition.NATIVE_SORT_ORDER);
}
for (PropertyDefinition functionIndex :
rule.getFunctionRestrictions()) {
- if (o.getPropertyName().equals(functionIndex.function)) {
+ if (functionIndex.ordered &&
o.getPropertyName().equals(functionIndex.function)) {
// can manage any order desc/asc
orderEntries.add(o);
result.sortedProperties.add(functionIndex);
Modified:
jackrabbit/oak/branches/1.22/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.22/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java?rev=1873427&r1=1873426&r2=1873427&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.22/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
(original)
+++
jackrabbit/oak/branches/1.22/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
Fri Jan 31 18:21:15 2020
@@ -30,6 +30,7 @@ import org.apache.jackrabbit.oak.api.Res
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
import org.apache.jackrabbit.oak.commons.json.JsopWriter;
+import org.apache.jackrabbit.oak.plugins.index.search.FieldNames;
import org.apache.jackrabbit.oak.plugins.index.search.util.LMSEstimator;
import
org.apache.jackrabbit.oak.plugins.index.solr.configuration.OakSolrConfiguration;
import
org.apache.jackrabbit.oak.plugins.index.solr.configuration.OakSolrConfigurationProvider;
@@ -579,12 +580,24 @@ public class SolrQueryIndex implements F
private IndexPlan getIndexPlan(Filter filter, OakSolrConfiguration
configuration, LMSEstimator estimator,
List<OrderEntry> sortOrder, String path) {
if (getMatchingFilterRestrictions(filter, configuration) > 0) {
+
+ // we can't order by functions
+ // so remove those entries from the plan's sort order
+ ArrayList<OrderEntry> sortOrder2 = new ArrayList<>();
+ if (sortOrder != null) {
+ for (OrderEntry e : sortOrder) {
+ if
(!e.getPropertyName().startsWith(FieldNames.FUNCTION_PREFIX)) {
+ sortOrder2.add(e);
+ }
+ }
+ }
+
IndexPlan indexPlan = planBuilder(filter)
- .setEstimatedEntryCount(estimator.estimate(filter))
- .setSortOrder(sortOrder)
- .setPlanName(path)
- .setPathPrefix(getPathPrefix(path))
- .build();
+ .setEstimatedEntryCount(estimator.estimate(filter))
+ .setSortOrder(sortOrder2)
+ .setPlanName(path)
+ .setPathPrefix(getPathPrefix(path))
+ .build();
log.debug("index plan {}", indexPlan);
return indexPlan;
} else {