Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java (revision 1039888) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java (revision ) @@ -16,18 +16,6 @@ */ package org.apache.jackrabbit.core.query.lucene; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.jcr.RepositoryException; -import javax.jcr.Value; -import javax.jcr.Workspace; -import javax.jcr.nodetype.PropertyDefinition; -import javax.jcr.query.InvalidQueryException; -import javax.jcr.query.QueryResult; -import javax.jcr.query.qom.QueryObjectModelFactory; - import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.nodetype.NodeTypeImpl; import org.apache.jackrabbit.core.query.PropertyTypeRegistry; @@ -51,6 +39,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.jcr.RepositoryException; +import javax.jcr.Workspace; +import javax.jcr.nodetype.PropertyDefinition; +import javax.jcr.query.InvalidQueryException; +import javax.jcr.query.QueryResult; +import javax.jcr.query.qom.QueryObjectModelFactory; +import java.util.LinkedHashMap; +import java.util.Map; + /** * Implements the {@link org.apache.jackrabbit.core.query.ExecutableQuery} * interface. @@ -126,15 +123,17 @@ } Path[] orderProperties = new Path[orderSpecs.length]; boolean[] ascSpecs = new boolean[orderSpecs.length]; + String[] orderFuncs = new String[orderSpecs.length]; for (int i = 0; i < orderSpecs.length; i++) { orderProperties[i] = orderSpecs[i].getPropertyPath(); ascSpecs[i] = orderSpecs[i].isAscending(); + orderFuncs[i] = orderSpecs[i].getFunction(); } return new SingleColumnQueryResult( index, sessionContext, this, query, new SpellSuggestion(index.getSpellChecker(), root), - getColumns(), orderProperties, ascSpecs, + getColumns(), orderProperties, ascSpecs, orderFuncs, orderProperties.length == 0 && getRespectDocumentOrder(), offset, limit); } Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java =================================================================== --- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java (revision 995787) +++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java (revision ) @@ -16,16 +16,6 @@ */ package org.apache.jackrabbit.spi.commons.query.xpath; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.Map; - -import javax.jcr.NamespaceException; -import javax.jcr.RepositoryException; -import javax.jcr.query.InvalidQueryException; - import org.apache.commons.collections.map.ReferenceMap; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.NameFactory; @@ -57,6 +47,15 @@ import org.apache.jackrabbit.util.ISO8601; import org.apache.jackrabbit.util.ISO9075; +import javax.jcr.NamespaceException; +import javax.jcr.RepositoryException; +import javax.jcr.query.InvalidQueryException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; + /** * Query builder that translates a XPath statement into a query tree structure. */ @@ -439,7 +438,7 @@ || queryNode.getType() == QueryNode.TYPE_PATH) { createNodeTest(node, queryNode); } else if (queryNode.getType() == QueryNode.TYPE_ORDER) { - createOrderSpec(node, (OrderQueryNode) queryNode); + setOrderSpecPath(node, (OrderQueryNode) queryNode); } else { // traverse node.childrenAccept(this, queryNode); @@ -523,11 +522,15 @@ queryNode = root.getOrderNode(); node.childrenAccept(this, queryNode); break; + case JJTORDERSPECLIST: + OrderQueryNode orderQueryNode = (OrderQueryNode) queryNode; + orderQueryNode.newOrderSpec(); + node.childrenAccept(this, queryNode); + break; case JJTORDERMODIFIER: if (node.jjtGetNumChildren() > 0 && ((SimpleNode) node.jjtGetChild(0)).getId() == JJTDESCENDING) { - OrderQueryNode.OrderSpec[] specs = ((OrderQueryNode) queryNode).getOrderSpecs(); - specs[specs.length - 1].setAscending(false); + ((OrderQueryNode) queryNode).setAscending(false); } break; case JJTPREDICATELIST: @@ -962,7 +965,7 @@ } } else if (JCR_SCORE.equals(funName)) { if (queryNode.getType() == QueryNode.TYPE_ORDER) { - createOrderSpec(node, (OrderQueryNode) queryNode); + setOrderSpecPath(node, (OrderQueryNode) queryNode); } else { exceptions.add(new InvalidQueryException("Unsupported location for jcr:score()")); } @@ -974,6 +977,9 @@ relNode, PropertyFunctionQueryNode.LOWER_CASE)); // get property name node.jjtGetChild(1).jjtAccept(this, relNode); + } else if (queryNode.getType() == QueryNode.TYPE_ORDER) { + ((OrderQueryNode) queryNode).setFunction(FN_LOWER_CASE.getLocalName()); + node.childrenAccept(this, queryNode); } else { exceptions.add(new InvalidQueryException("Unsupported location for fn:lower-case()")); } @@ -988,6 +994,9 @@ relNode, PropertyFunctionQueryNode.UPPER_CASE)); // get property name node.jjtGetChild(1).jjtAccept(this, relNode); + } else if (queryNode.getType() == QueryNode.TYPE_ORDER) { + ((OrderQueryNode) queryNode).setFunction(FN_UPPER_CASE.getLocalName()); + node.childrenAccept(this, queryNode); } else { exceptions.add(new InvalidQueryException("Unsupported location for fn:upper-case()")); } @@ -1120,10 +1129,8 @@ return derefNode; } - private OrderQueryNode.OrderSpec createOrderSpec(SimpleNode node, - OrderQueryNode queryNode) { + private void setOrderSpecPath(SimpleNode node, OrderQueryNode queryNode) { SimpleNode child = (SimpleNode) node.jjtGetChild(0); - OrderQueryNode.OrderSpec spec = null; try { String propName = child.getValue(); if (child.getId() == JJTQNAMELPAR) { @@ -1139,14 +1146,12 @@ } else { path = PathFactoryImpl.getInstance().create(element); } - spec = new OrderQueryNode.OrderSpec(path, true); - queryNode.addOrderSpec(spec); + queryNode.setPath(path); } catch (NameException e) { exceptions.add(new InvalidQueryException("Illegal name: " + child.getValue())); } catch (NamespaceException e) { exceptions.add(new InvalidQueryException("Illegal name: " + child.getValue())); } - return spec; } /** Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java (revision 983708) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResult.java (revision ) @@ -16,15 +16,14 @@ */ package org.apache.jackrabbit.core.query.lucene; -import java.io.IOException; - -import javax.jcr.RepositoryException; - import org.apache.jackrabbit.core.session.SessionContext; import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.commons.query.qom.ColumnImpl; import org.apache.lucene.search.Query; +import javax.jcr.RepositoryException; +import java.io.IOException; + /** * SingleColumnQueryResult implements a query result that returns * a single column. That is, executes a lucene query. @@ -45,18 +44,20 @@ * The order specifier for each of the order properties. */ protected final boolean[] orderSpecs; + private String[] orderFuncs; public SingleColumnQueryResult( SearchIndex index, SessionContext sessionContext, AbstractQueryImpl queryImpl, Query query, SpellSuggestion spellSuggestion, ColumnImpl[] columns, - Path[] orderProps, boolean[] orderSpecs, boolean documentOrder, + Path[] orderProps, boolean[] orderSpecs, String[] orderFuncs, boolean documentOrder, long offset, long limit) throws RepositoryException { super(index, sessionContext, queryImpl, spellSuggestion, columns, documentOrder, offset, limit); this.query = query; this.orderProps = orderProps; this.orderSpecs = orderSpecs; + this.orderFuncs = orderFuncs; // if document order is requested get all results right away getResults(docOrder ? Integer.MAX_VALUE : index.getResultFetchSize()); } @@ -68,7 +69,7 @@ throws IOException { return index.executeQuery( sessionContext.getSessionImpl(), queryImpl, query, - orderProps, orderSpecs, resultFetchHint); + orderProps, orderSpecs, orderFuncs, resultFetchHint); } /** Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (revision 1039888) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (revision ) @@ -769,8 +769,8 @@ * @param orderSpecs the order specs for the sort order properties. * true indicates ascending order, * false indicates descending. - * @param resultFetchHint a hint on how many results should be fetched. - * @return the query hits. + * @param orderFuncs + *@param resultFetchHint a hint on how many results should be fetched. @return the query hits. * @throws IOException if an error occurs while searching the index. */ public MultiColumnQueryHits executeQuery(SessionImpl session, @@ -778,11 +778,11 @@ Query query, Path[] orderProps, boolean[] orderSpecs, - long resultFetchHint) + String[] orderFuncs, long resultFetchHint) throws IOException { checkOpen(); - Sort sort = new Sort(createSortFields(orderProps, orderSpecs)); + Sort sort = new Sort(createSortFields(orderProps, orderSpecs, orderFuncs)); final IndexReader reader = getIndexReader(queryImpl.needsSystemTree()); JackrabbitIndexSearcher searcher = new JackrabbitIndexSearcher( @@ -1016,10 +1016,11 @@ * * @param orderProps the order properties. * @param orderSpecs the order specs for the properties. + * @param orderFuncs * @return an array of sort fields */ protected SortField[] createSortFields(Path[] orderProps, - boolean[] orderSpecs) { + boolean[] orderSpecs, String[] orderFuncs) { List sortFields = new ArrayList(); for (int i = 0; i < orderProps.length; i++) { if (orderProps[i].getLength() == 1 @@ -1030,9 +1031,15 @@ // are first. sortFields.add(new SortField(null, SortField.SCORE, orderSpecs[i])); } else { + if ("upper-case".equals(orderFuncs[i])) { + sortFields.add(new SortField(orderProps[i].getString(), new UpperCaseSortComparator(scs), !orderSpecs[i])); + } else if ("lower-case".equals(orderFuncs[i])) { + sortFields.add(new SortField(orderProps[i].getString(), new LowerCaseSortComparator(scs), !orderSpecs[i])); + } else { - sortFields.add(new SortField(orderProps[i].getString(), scs, !orderSpecs[i])); - } - } + sortFields.add(new SortField(orderProps[i].getString(), scs, !orderSpecs[i])); + } + } + } return sortFields.toArray(new SortField[sortFields.size()]); } Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java =================================================================== --- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java (revision 997303) +++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/OrderQueryNode.java (revision ) @@ -16,16 +16,15 @@ */ package org.apache.jackrabbit.spi.commons.query; -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.RepositoryException; - import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.Path; -import org.apache.jackrabbit.spi.commons.name.PathBuilder; import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException; +import org.apache.jackrabbit.spi.commons.name.PathBuilder; +import javax.jcr.RepositoryException; +import java.util.ArrayList; +import java.util.List; + /** * Implements a query node that defines the order of nodes according to the * values of properties. @@ -56,6 +55,25 @@ return QueryNode.TYPE_ORDER; } + public void newOrderSpec() { + specs.add(new OrderSpec((Path) null, true)); + } + + public void setAscending(boolean value) { + OrderSpec orderSpec = (OrderSpec) specs.get(specs.size() - 1); + orderSpec.setAscending(value); + } + + public void setPath(Path path) { + OrderSpec orderSpec = (OrderSpec) specs.get(specs.size() - 1); + orderSpec.setPath(path); + } + + public void setFunction(String name) { + OrderSpec orderSpec = (OrderSpec) specs.get(specs.size() - 1); + orderSpec.setFunction(name); + } + /** * Adds an order specification to this query node. * @@ -141,12 +159,13 @@ /** * The relative path to of the property */ - private final Path property; + private Path property; /** * If true this property is orderd ascending */ private boolean ascending; + private String function; /** * Creates a new OrderSpec for property. @@ -212,6 +231,18 @@ this.ascending = ascending; } + public void setPath(Path path) { + this.property = path; + } + + public void setFunction(String name) { + this.function = name; + } + + public String getFunction() { + return function; + } + /** * Returns true if this order spec is equal * to obj @@ -227,6 +258,7 @@ } return false; } + } /**