jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r744889 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/query/ main/java/org/apache/jackrabbit/core/query/lucene/ test/java/org/apache/jackrabbit/core/query/
Date Mon, 16 Feb 2009 10:51:44 GMT
Author: mreutegg
Date: Mon Feb 16 10:51:37 2009
New Revision: 744889

URL: http://svn.apache.org/viewvc?rev=744889&view=rev
Log:
JCR-800: Child Axis support in order by clause

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandlerContext.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandlerContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandlerContext.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandlerContext.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandlerContext.java
Mon Feb 16 10:51:37 2009
@@ -21,6 +21,8 @@
 import org.apache.jackrabbit.core.state.ItemStateManager;
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.NamespaceRegistryImpl;
+import org.apache.jackrabbit.core.HierarchyManager;
+import org.apache.jackrabbit.core.HierarchyManagerImpl;
 import org.apache.jackrabbit.core.persistence.PersistenceManager;
 
 /**
@@ -41,6 +43,11 @@
     private final ItemStateManager stateMgr;
 
     /**
+     * The hierarchy manager on top of {@link #stateMgr}.
+     */
+    private final HierarchyManager hmgr;
+
+    /**
      * The underlying persistence manager.
      */
     private final PersistenceManager pm;
@@ -103,6 +110,7 @@
                                NodeId excludedNodeId) {
         this.fs = fs;
         this.stateMgr = stateMgr;
+        this.hmgr = new HierarchyManagerImpl(rootId, stateMgr);
         this.pm = pm;
         this.rootId = rootId;
         this.ntRegistry = ntRegistry;
@@ -125,6 +133,16 @@
     }
 
     /**
+     * Returns the hierarchy manager on top of the item state manager of this
+     * query handler context.
+     *
+     * @return the hierarchy manager.
+     */
+    public HierarchyManager getHierarchyManager() {
+        return hmgr;
+    }
+
+    /**
      * @return the underlying persistence manager.
      */
     public PersistenceManager getPersistenceManager() {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
Mon Feb 16 10:51:37 2009
@@ -31,6 +31,7 @@
 import org.apache.jackrabbit.core.nodetype.PropertyDefinitionImpl;
 import org.apache.jackrabbit.core.query.PropertyTypeRegistry;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.apache.jackrabbit.spi.commons.query.AndQueryNode;
@@ -120,10 +121,10 @@
         } else {
             orderSpecs = new OrderQueryNode.OrderSpec[0];
         }
-        Name[] orderProperties = new Name[orderSpecs.length];
+        Path[] orderProperties = new Path[orderSpecs.length];
         boolean[] ascSpecs = new boolean[orderSpecs.length];
         for (int i = 0; i < orderSpecs.length; i++) {
-            orderProperties[i] = orderSpecs[i].getProperty();
+            orderProperties[i] = orderSpecs[i].getPropertyPath();
             ascSpecs[i] = orderSpecs[i].isAscending();
         }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java
Mon Feb 16 10:51:37 2009
@@ -18,18 +18,22 @@
 
 import org.apache.jackrabbit.core.query.PropertyTypeRegistry;
 import org.apache.jackrabbit.spi.commons.query.jsr283.qom.QueryObjectModelConstants;
+import org.apache.jackrabbit.spi.commons.query.jsr283.qom.PropertyValue;
 import org.apache.jackrabbit.spi.commons.query.qom.QueryObjectModelTree;
 import org.apache.jackrabbit.spi.commons.query.qom.ColumnImpl;
 import org.apache.jackrabbit.spi.commons.query.qom.OrderingImpl;
 import org.apache.jackrabbit.spi.commons.query.qom.DefaultTraversingQOMTreeVisitor;
 import org.apache.jackrabbit.spi.commons.query.qom.BindVariableValueImpl;
 import org.apache.jackrabbit.spi.commons.query.qom.SelectorImpl;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.ItemManager;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.lucene.search.Query;
 
 import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.query.QueryResult;
 
 /**
@@ -81,7 +85,7 @@
         Name[] names = new Name[selectors.length];
         for (int i = 0; i < names.length; i++) {
             names[i] = selectors[i].getSelectorQName();
-        };
+        }
         return names;
     }
 
@@ -110,10 +114,17 @@
         }
         OrderingImpl[] orderings = qomTree.getOrderings();
         // TODO: there are many kinds of DynamicOperand that can be ordered by
-        Name[] orderProps = new Name[orderings.length];
+        Path[] orderProps = new Path[orderings.length];
         boolean[] orderSpecs = new boolean[orderings.length];
         for (int i = 0; i < orderings.length; i++) {
             orderSpecs[i] = orderings[i].getOrder() == QueryObjectModelConstants.ORDER_ASCENDING;
+            if (orderings[i].getOperand() instanceof PropertyValue) {
+                PropertyValue pv = (PropertyValue) orderings[i].getOperand();
+                orderProps[i] = PathFactoryImpl.getInstance().create(pv.getPropertyName());
+            } else {
+                throw new UnsupportedRepositoryOperationException("order by with" +
+                        orderings[i].getOperand() + " not yet implemented");
+            }
         }
         return new QueryResultImpl(index, itemMgr,
                 session, session.getAccessManager(),

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java
Mon Feb 16 10:51:37 2009
@@ -21,6 +21,7 @@
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.security.AccessManager;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 import org.apache.lucene.search.Query;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -88,9 +89,9 @@
     protected final Name[] selectProps;
 
     /**
-     * The names of properties to use for ordering the result set.
+     * The relative paths of properties to use for ordering the result set.
      */
-    protected final Name[] orderProps;
+    protected final Path[] orderProps;
 
     /**
      * The order specifier for each of the order properties.
@@ -153,7 +154,7 @@
      * @param spellSuggestion the spell suggestion or <code>null</code> if none
      *                        is available.
      * @param selectProps     the select properties of the query.
-     * @param orderProps      the names of the order properties.
+     * @param orderProps      the relative paths of the order properties.
      * @param orderSpecs      the order specs, one for each order property
      *                        name.
      * @param documentOrder   if <code>true</code> the result is returned in
@@ -169,7 +170,7 @@
                            Query query,
                            SpellSuggestion spellSuggestion,
                            Name[] selectProps,
-                           Name[] orderProps,
+                           Path[] orderProps,
                            boolean[] orderSpecs,
                            boolean documentOrder,
                            long offset,

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
Mon Feb 16 10:51:37 2009
@@ -35,7 +35,6 @@
 import org.apache.jackrabbit.core.state.ItemStateManager;
 import org.apache.jackrabbit.extractor.DefaultTextExtractor;
 import org.apache.jackrabbit.extractor.TextExtractor;
-import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PathFactory;
@@ -55,6 +54,7 @@
 import org.apache.lucene.search.Sort;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.SortComparatorSource;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.Fieldable;
@@ -66,7 +66,6 @@
 import org.w3c.dom.Element;
 
 import javax.jcr.RepositoryException;
-import javax.jcr.NamespaceException;
 import javax.jcr.query.InvalidQueryException;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.DocumentBuilder;
@@ -213,11 +212,6 @@
     private NamespaceMappings nsMappings;
 
     /**
-     * The name and path resolver used internally.
-     */
-    private NamePathResolver npResolver;
-
-    /**
      * The location of the search index.
      * <p/>
      * Note: This is a <b>mandatory</b> parameter!
@@ -427,6 +421,11 @@
     private int termInfosIndexDivisor = DEFAULT_TERM_INFOS_INDEX_DIVISOR;
 
     /**
+     * The sort comparator source for indexed properties.
+     */
+    private SortComparatorSource scs;
+
+    /**
      * Indicates if this <code>SearchIndex</code> is closed and cannot be used
      * anymore.
      */
@@ -479,8 +478,10 @@
                         context.getNamespaceRegistry());
             }
         }
-        npResolver = NamePathResolverImpl.create(nsMappings);
 
+        scs = new SharedFieldSortComparator(
+                FieldNames.PROPERTIES, context.getItemStateManager(),
+                context.getHierarchyManager(), nsMappings);
         indexingConfig = createIndexingConfiguration(nsMappings);
         analyzer.setIndexingConfig(indexingConfig);
 
@@ -728,7 +729,7 @@
     public MultiColumnQueryHits executeQuery(SessionImpl session,
                                   AbstractQueryImpl queryImpl,
                                   Query query,
-                                  Name[] orderProps,
+                                  Path[] orderProps,
                                   boolean[] orderSpecs) throws IOException {
         checkOpen();
 
@@ -913,24 +914,19 @@
      * @param orderSpecs the order specs for the properties.
      * @return an array of sort fields
      */
-    protected SortField[] createSortFields(Name[] orderProps,
+    protected SortField[] createSortFields(Path[] orderProps,
                                            boolean[] orderSpecs) {
         List sortFields = new ArrayList();
         for (int i = 0; i < orderProps.length; i++) {
-            String prop = null;
-            if (NameConstants.JCR_SCORE.equals(orderProps[i])) {
+            if (orderProps[i].getLength() == 1
+                    && NameConstants.JCR_SCORE.equals(orderProps[i].getNameElement().getName()))
{
                 // order on jcr:score does not use the natural order as
                 // implemented in lucene. score ascending in lucene means that
                 // higher scores are first. JCR specs that lower score values
                 // are first.
                 sortFields.add(new SortField(null, SortField.SCORE, orderSpecs[i]));
             } else {
-                try {
-                    prop = npResolver.getJCRName(orderProps[i]);
-                } catch (NamespaceException e) {
-                    // will never happen
-                }
-                sortFields.add(new SortField(prop, SharedFieldSortComparator.PROPERTIES,
!orderSpecs[i]));
+                sortFields.add(new SortField(orderProps[i].getString(), scs, !orderSpecs[i]));
             }
         }
         return (SortField[]) sortFields.toArray(new SortField[sortFields.size()]);

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java
Mon Feb 16 10:51:37 2009
@@ -26,8 +26,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.WeakHashMap;
-import java.util.List;
-import java.util.ArrayList;
 
 /**
  * Implements a variant of the lucene class <code>org.apache.lucene.search.FieldCacheImpl</code>.
@@ -50,11 +48,6 @@
         private static final int SPARSE_FACTOR = 100;
 
         /**
-         * All the term values, in natural order.
-         */
-        public final String[] lookup;
-
-        /**
          * Terms indexed by document id.
          */
         private final String[] terms;
@@ -72,7 +65,7 @@
         /**
          * Creates one of these objects
          */
-        public StringIndex(String[] terms, String[] lookup, int setValues) {
+        public StringIndex(String[] terms, int setValues) {
             if (isSparse(terms, setValues)) {
                 this.sparse = true;
                 this.terms = null;
@@ -86,7 +79,6 @@
                 this.terms = terms;
                 this.termsMap = null;
             }
-            this.lookup = lookup;
         }
 
         public String getTerm(int i) {
@@ -147,7 +139,6 @@
      * @param field      name of the shared field.
      * @param prefix     the property name, will be used as term prefix.
      * @param comparator the sort comparator instance.
-     * @param includeLookup if <code>true</code> provides term lookup in StringIndex.
      * @return a StringIndex that contains the field values and order
      *         information.
      * @throws IOException if an error occurs while reading from the index.
@@ -155,8 +146,7 @@
     public SharedFieldCache.StringIndex getStringIndex(IndexReader reader,
                                                  String field,
                                                  String prefix,
-                                                 SortComparator comparator,
-                                                 boolean includeLookup)
+                                                 SortComparator comparator)
             throws IOException {
 
         if (reader instanceof ReadOnlyIndexReader) {
@@ -167,21 +157,12 @@
         SharedFieldCache.StringIndex ret = lookup(reader, field, prefix, comparator);
         if (ret == null) {
             final String[] retArray = new String[reader.maxDoc()];
-            List mterms = null;
-            if (includeLookup) {
-                mterms = new ArrayList();
-            }
             int setValues = 0;
             if (retArray.length > 0) {
                 TermDocs termDocs = reader.termDocs();
                 TermEnum termEnum = reader.terms(new Term(field, prefix));
-                // documents without a term will have a term number = 0
-                // thus will be at the top, this needs to be in sync with
-                // the implementation of FieldDocSortedHitQueue
-                if (includeLookup) {
-                    mterms.add(null); // for documents with term number 0
-                }
 
+                char[] tmp = new char[16];
                 try {
                     if (termEnum.term() == null) {
                         throw new RuntimeException("no terms in field " + field);
@@ -192,15 +173,20 @@
                             break;
                         }
 
-                        // store term text
-                        if (includeLookup) {
-                            mterms.add(term.text().substring(prefix.length()));
+                        // make sure term is compacted
+                        String text = term.text();
+                        int len = text.length() - prefix.length();
+                        if (tmp.length < len) {
+                            // grow tmp
+                            tmp = new char[len];
                         }
+                        text.getChars(prefix.length(), text.length(), tmp, 0);
+                        String value = new String(tmp, 0, len);
 
                         termDocs.seek(termEnum);
                         while (termDocs.next()) {
                             setValues++;
-                            retArray[termDocs.doc()] = term.text().substring(prefix.length());
+                            retArray[termDocs.doc()] = value;
                         }
                     } while (termEnum.next());
                 } finally {
@@ -208,11 +194,7 @@
                     termEnum.close();
                 }
             }
-            String[] lookup = null;
-            if (includeLookup) {
-                lookup = (String[]) mterms.toArray(new String[mterms.size()]);
-            }
-            SharedFieldCache.StringIndex value = new SharedFieldCache.StringIndex(retArray,
lookup, setValues);
+            SharedFieldCache.StringIndex value = new SharedFieldCache.StringIndex(retArray,
setValues);
             store(reader, field, prefix, comparator, value);
             return value;
         }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java
Mon Feb 16 10:51:37 2009
@@ -20,12 +20,26 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.jackrabbit.core.query.lucene.SharedFieldCache.StringIndex;
+import javax.jcr.PropertyType;
+
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.ScoreDocComparator;
 import org.apache.lucene.search.SortComparator;
 import org.apache.lucene.search.SortField;
+import org.apache.lucene.document.Document;
+import org.apache.jackrabbit.core.state.ItemStateManager;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.HierarchyManager;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.commons.name.PathBuilder;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.uuid.UUID;
 
 /**
  * Implements a <code>SortComparator</code> which knows how to sort on a lucene
@@ -41,146 +55,69 @@
 public class SharedFieldSortComparator extends SortComparator {
 
     /**
-     * A <code>SharedFieldSortComparator</code> that is based on
-     * {@link FieldNames#PROPERTIES}.
+     * The name of the shared field in the lucene index.
      */
-    static final SortComparator PROPERTIES = new SharedFieldSortComparator(FieldNames.PROPERTIES);
+    private final String field;
 
     /**
-     * The name of the shared field in the lucene index.
+     * The item state manager.
      */
-    private final String field;
+    private final ItemStateManager ism;
 
     /**
-     * If <code>true</code> <code>ScoreDocComparator</code> will
returns term
-     * values when {@link org.apache.lucene.search.ScoreDocComparator#sortValue(org.apache.lucene.search.ScoreDoc)}
-     * is called, otherwise only a dummy value is returned.
+     * The hierarchy manager on top of {@link #ism}.
      */
-    private final boolean createComparatorValues;
+    private final HierarchyManager hmgr;
 
     /**
-     * Creates a new <code>SharedFieldSortComparator</code> for a given shared
-     * field.
-     *
-     * @param fieldname the shared field.
+     * The index internal namespace mappings.
      */
-    public SharedFieldSortComparator(String fieldname) {
-        this(fieldname, false);
-    }
+    private final NamespaceMappings nsMappings;
 
     /**
      * Creates a new <code>SharedFieldSortComparator</code> for a given shared
      * field.
      *
-     * @param fieldname              the shared field.
-     * @param createComparatorValues if <code>true</code> creates values
-     * for the <code>ScoreDocComparator</code>s.
-     * @see #createComparatorValues
+     * @param fieldname the shared field.
+     * @param ism       the item state manager of this workspace.
+     * @param hmgr      the hierarchy manager of this workspace.
+     * @param nsMappings the index internal namespace mappings.
      */
-    public SharedFieldSortComparator(String fieldname, boolean createComparatorValues) {
+    public SharedFieldSortComparator(String fieldname,
+                                     ItemStateManager ism,
+                                     HierarchyManager hmgr,
+                                     NamespaceMappings nsMappings) {
         this.field = fieldname;
-        this.createComparatorValues = createComparatorValues;
+        this.ism = ism;
+        this.hmgr = hmgr;
+        this.nsMappings = nsMappings;
     }
 
     /**
      * Creates a new <code>ScoreDocComparator</code> for an embedded
      * <code>propertyName</code> and a <code>reader</code>.
+     *
      * @param reader the index reader.
-     * @param propertyName the name of the property to sort.
+     * @param relPath the relative path to the property to sort on as returned
+     *          by {@link Path#getString()}.
      * @return a <code>ScoreDocComparator</code> for the
-     * @throws IOException
-     * @throws IOException
+     * @throws IOException if an error occurs while reading from the index.
      */
-    public ScoreDocComparator newComparator(final IndexReader reader, final String propertyName)
throws IOException {
-
-        final List readers = new ArrayList();
-        getIndexReaders(readers, reader);
-
-        final SharedFieldCache.StringIndex[] indexes = new SharedFieldCache.StringIndex[readers.size()];
-
-        int maxDoc = 0;
-        final int[] starts = new int[readers.size() + 1];
-
-        for (int i = 0; i < readers.size(); i++) {
-            IndexReader r = (IndexReader) readers.get(i);
-            starts[i] = maxDoc;
-            maxDoc += r.maxDoc();
-            indexes[i] = SharedFieldCache.INSTANCE.getStringIndex(r, field,
-                    FieldNames.createNamedValue(propertyName, ""),
-                    SharedFieldSortComparator.this, createComparatorValues);
-        }
-        starts[readers.size()] = maxDoc;
-
-        return new ScoreDocComparator() {
-
-            public final int compare(final ScoreDoc i, final ScoreDoc j) {
-                int idx1 = readerIndex(i.doc);
-                int idx2 = readerIndex(j.doc);
-
-                String iTerm = indexes[idx1].getTerm(i.doc - starts[idx1]);
-                String jTerm = indexes[idx2].getTerm(j.doc - starts[idx2]);
-
-                if (iTerm == jTerm) {
-                    return 0;
-                } else if (iTerm == null) {
-                    return -1;
-                } else if (jTerm == null) {
-                    return 1;
-                } else {
-                    return iTerm.compareTo(jTerm);
-                }
+    public ScoreDocComparator newComparator(IndexReader reader,
+                                            String relPath)
+            throws IOException {
+        PathFactory factory = PathFactoryImpl.getInstance();
+        Path p = factory.create(relPath);
+        if (p.getLength() == 1) {
+            try {
+                return new SimpleScoreDocComparator(reader,
+                        nsMappings.translatePropertyName(p.getNameElement().getName()));
+            } catch (IllegalNameException e) {
+                throw Util.createIOException(e);
             }
-
-            /**
-             * Returns an empty if no lookup table is available otherwise the
-             * index term for the score doc <code>i</code>.
-             *
-             * @param i
-             *            the score doc.
-             * @return the sort value if available.
-             */
-            public Comparable sortValue(final ScoreDoc i) {
-                if (createComparatorValues) {
-                    int idx = readerIndex(i.doc);
-                    return indexes[idx].getTerm(i.doc - starts[idx]);
-                } else {
-                    // return dummy value
-                    return "";
-                }
-            }
-
-            public int sortType() {
-                return SortField.CUSTOM;
-            }
-
-            /**
-             * Returns the reader index for document <code>n</code>.
-             *
-             * @param n document number.
-             * @return the reader index.
-             */
-            private int readerIndex(int n) {
-                int lo = 0;
-                int hi = readers.size() - 1;
-
-                while (hi >= lo) {
-                    int mid = (lo + hi) >> 1;
-                    int midValue = starts[mid];
-                    if (n < midValue) {
-                        hi = mid - 1;
-                    } else if (n > midValue) {
-                        lo = mid + 1;
-                    } else {
-                        while (mid + 1 < readers.size() && starts[mid + 1] ==
midValue) {
-                            mid++;
-                        }
-                        return mid;
-                    }
-                }
-                return hi;
-            }
-
-        };
+        } else {
+            return new RelPathScoreDocComparator(reader, p);
+        }
     }
 
     /**
@@ -198,7 +135,7 @@
      * @param readers the list of index readers.
      * @param reader  the reader to check.
      */
-    private void getIndexReaders(List readers, IndexReader reader) {
+    private static void getIndexReaders(List readers, IndexReader reader) {
         if (reader instanceof MultiIndexReader) {
             IndexReader[] r = ((MultiIndexReader) reader).getIndexReaders();
             for (int i = 0; i < r.length; i++) {
@@ -208,4 +145,242 @@
             readers.add(reader);
         }
     }
+
+    /**
+     * Abstract base class of {@link ScoreDocComparator} implementations.
+     */
+    abstract class AbstractScoreDocComparator implements ScoreDocComparator {
+
+        /**
+         * The index readers.
+         */
+        protected final List readers = new ArrayList();
+
+        /**
+         * The document number starts for the {@link #readers}.
+         */
+        protected final int[] starts;
+
+        public AbstractScoreDocComparator(IndexReader reader)
+                throws IOException {
+            getIndexReaders(readers, reader);
+
+            int maxDoc = 0;
+            this.starts = new int[readers.size() + 1];
+
+            for (int i = 0; i < readers.size(); i++) {
+                IndexReader r = (IndexReader) readers.get(i);
+                starts[i] = maxDoc;
+                maxDoc += r.maxDoc();
+            }
+            starts[readers.size()] = maxDoc;
+        }
+
+        /**
+         * Compares sort values of <code>i</code> and <code>j</code>.
If the
+         * sort values have differing types, then the sort order is defined on
+         * the type itself by calling <code>compareTo()</code> on the respective
+         * type class names.
+         *
+         * @param i first score doc.
+         * @param j second score doc.
+         * @return a negative integer if <code>i</code> should come before
+         *         <code>j</code><br> a positive integer if <code>i</code>
+         *         should come after <code>j</code><br> <code>0</code>
if they
+         *         are equal
+         */
+        public int compare(ScoreDoc i, ScoreDoc j) {
+            Comparable iTerm = sortValue(i);
+            Comparable jTerm = sortValue(j);
+
+            if (iTerm == jTerm) {
+                return 0;
+            } else if (iTerm == null) {
+                return -1;
+            } else if (jTerm == null) {
+                return 1;
+            } else if (iTerm.getClass() == jTerm.getClass()) {
+                return iTerm.compareTo(jTerm);
+            } else {
+                // differing types -> compare class names
+                String iName = iTerm.getClass().getName();
+                String jName = jTerm.getClass().getName();
+                return iName.compareTo(jName);
+            }
+        }
+
+        public int sortType() {
+            return SortField.CUSTOM;
+        }
+
+        /**
+         * Returns the reader index for document <code>n</code>.
+         *
+         * @param n document number.
+         * @return the reader index.
+         */
+        protected int readerIndex(int n) {
+            int lo = 0;
+            int hi = readers.size() - 1;
+
+            while (hi >= lo) {
+                int mid = (lo + hi) >> 1;
+                int midValue = starts[mid];
+                if (n < midValue) {
+                    hi = mid - 1;
+                } else if (n > midValue) {
+                    lo = mid + 1;
+                } else {
+                    while (mid + 1 < readers.size() && starts[mid + 1] == midValue)
{
+                        mid++;
+                    }
+                    return mid;
+                }
+            }
+            return hi;
+        }
+    }
+
+    /**
+     * A score doc comparator that works for order by clauses with properties
+     * directly on the result nodes.
+     */
+    private final class SimpleScoreDocComparator extends AbstractScoreDocComparator {
+
+        /**
+         * The term look ups of the index segments.
+         */
+        protected final SharedFieldCache.StringIndex[] indexes;
+
+        public SimpleScoreDocComparator(IndexReader reader,
+                                        String propertyName)
+                throws IOException {
+            super(reader);
+            this.indexes = new SharedFieldCache.StringIndex[readers.size()];
+
+            for (int i = 0; i < readers.size(); i++) {
+                IndexReader r = (IndexReader) readers.get(i);
+                indexes[i] = SharedFieldCache.INSTANCE.getStringIndex(r, field,
+                        FieldNames.createNamedValue(propertyName, ""),
+                        SharedFieldSortComparator.this);
+            }
+        }
+
+        /**
+         * Returns the index term for the score doc <code>i</code>.
+         *
+         * @param i the score doc.
+         * @return the sort value if available.
+         */
+        public Comparable sortValue(ScoreDoc i) {
+            int idx = readerIndex(i.doc);
+            return indexes[idx].getTerm(i.doc - starts[idx]);
+        }
+    }
+
+    /**
+     * A score doc comparator that works with order by clauses that use a
+     * relative path to a property to sort on.
+     */
+    private final class RelPathScoreDocComparator extends AbstractScoreDocComparator {
+
+        private final Path relPath;
+
+        public RelPathScoreDocComparator(IndexReader reader,
+                                         Path relPath)
+                throws IOException {
+            super(reader);
+            this.relPath = relPath;
+        }
+
+        /**
+         * Returns the sort value for the given {@link ScoreDoc}. The value is
+         * retrieved from the item state manager.
+         *
+         * @param i the score doc.
+         * @return the sort value for the score doc.
+         */
+        public Comparable sortValue(ScoreDoc i) {
+            try {
+                int idx = readerIndex(i.doc);
+                IndexReader reader = (IndexReader) readers.get(idx);
+                Document doc = reader.document(i.doc - starts[idx], FieldSelectors.UUID);
+                String uuid = doc.get(FieldNames.UUID);
+                Path path = hmgr.getPath(new NodeId(UUID.fromString(uuid)));
+                PathBuilder builder = new PathBuilder(path);
+                builder.addAll(relPath.getElements());
+                PropertyId id = hmgr.resolvePropertyPath(builder.getPath());
+                if (id == null) {
+                    return null;
+                }
+                PropertyState state = (PropertyState) ism.getItemState(id);
+                if (state == null) {
+                    return null;
+                }
+                InternalValue[] values = state.getValues();
+                if (values.length > 0) {
+                    return getComparable(values[0]);
+                }
+                return null;
+            } catch (Exception e) {
+                return null;
+            }
+        }
+
+        /**
+         * Returns a comparable for the <code>value</code>.
+         *
+         * @param value an internal value.
+         * @return a comparable for the given <code>value</code>.
+         */
+        private Comparable getComparable(InternalValue value) {
+            switch (value.getType()) {
+                case PropertyType.BINARY:
+                    return null;
+                case PropertyType.BOOLEAN:
+                    return ComparableBoolean.valueOf(value.getBoolean());
+                case PropertyType.DATE:
+                    return new Long(value.getDate().getTimeInMillis());
+                case PropertyType.DOUBLE:
+                    return new Double(value.getDouble());
+                case PropertyType.LONG:
+                    return new Long(value.getLong());
+                case PropertyType.NAME:
+                    return value.getQName().toString();
+                case PropertyType.PATH:
+                    return value.getPath().toString();
+                case PropertyType.REFERENCE:
+                case PropertyType.STRING:
+                    return value.getString();
+                default:
+                    return null;
+            }
+        }
+    }
+
+    /**
+     * Represents a boolean that implement {@link Comparable}. This class can
+     * be removed when we move to Java 5.
+     */
+    private static final class ComparableBoolean implements Comparable {
+
+        private static final ComparableBoolean TRUE = new ComparableBoolean(true);
+
+        private static final ComparableBoolean FALSE = new ComparableBoolean(false);
+
+        private final boolean value;
+
+        private ComparableBoolean(boolean value) {
+            this.value = value;
+        }
+
+        public int compareTo(Object o) {
+            ComparableBoolean b = (ComparableBoolean) o;
+            return (b.value == value ? 0 : (value ? 1 : -1));
+        }
+
+        static ComparableBoolean valueOf(boolean value) {
+            return value ? TRUE : FALSE;
+        }
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java?rev=744889&r1=744888&r2=744889&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java
Mon Feb 16 10:51:37 2009
@@ -16,8 +16,17 @@
  */
 package org.apache.jackrabbit.core.query;
 
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Calendar;
+
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
+import javax.jcr.NodeIterator;
+import javax.jcr.Value;
+import javax.jcr.PropertyType;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryResult;
 
@@ -51,4 +60,208 @@
         result = q.execute();
         checkResult(result, 3);
     }
+
+    public void testChildAxisString() throws RepositoryException {
+        checkChildAxis(new Value[]{getValue("a"), getValue("b"), getValue("c")});
+    }
+
+    public void testChildAxisLong() throws RepositoryException {
+        checkChildAxis(new Value[]{getValue(1), getValue(2), getValue(3)});
+    }
+
+    public void testChildAxisDouble() throws RepositoryException {
+        checkChildAxis(new Value[]{getValue(1.0), getValue(2.0), getValue(3.0)});
+    }
+
+    public void testChildAxisBoolean() throws RepositoryException {
+        checkChildAxis(new Value[]{getValue(false), getValue(true)});
+    }
+
+    public void testChildAxisCalendar() throws RepositoryException {
+        Calendar c1 = Calendar.getInstance();
+        Calendar c2 = Calendar.getInstance();
+        c2.add(Calendar.MINUTE, 1);
+        Calendar c3 = Calendar.getInstance();
+        c3.add(Calendar.MINUTE, 2);
+        checkChildAxis(new Value[]{getValue(c1), getValue(c2), getValue(c3)});
+    }
+
+    public void testChildAxisName() throws RepositoryException {
+        checkChildAxis(new Value[]{getNameValue("a"), getNameValue("b"), getNameValue("c")});
+    }
+
+    public void testChildAxisPath() throws RepositoryException {
+        checkChildAxis(new Value[]{getPathValue("a"), getPathValue("b"), getPathValue("c")});
+    }
+
+    public void testChildAxisDeep() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        n1.addNode("a").addNode("b"); // no property
+        Node n2 = testRootNode.addNode("node2");
+        n2.addNode("a").addNode("b").addNode("c").setProperty("prop", "a");
+        Node n3 = testRootNode.addNode("node2");
+        n3.addNode("a").addNode("b").addNode("c").setProperty("prop", "b");
+        testRootNode.save();
+
+        List expected = Arrays.asList(new String[]{n1.getPath(), n2.getPath(), n3.getPath()});
+        String xpath = testPath + "/* order by a/b/c/@prop";
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+
+        // descending
+        Collections.reverse(expected);
+        xpath = testPath + "/* order by a/b/c/@prop descending";
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+    }
+
+    public void testChildAxisNoValue() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        n1.addNode("child").setProperty("prop", "a");
+        Node n2 = testRootNode.addNode("node2");
+        n2.addNode("child");
+        testRootNode.save();
+
+        List expected = Arrays.asList(new String[]{n2.getPath(), n1.getPath()});
+        String xpath = testPath + "/* order by child/@prop";
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+
+        // descending
+        Collections.reverse(expected);
+        xpath = testPath + "/* order by child/@prop descending";
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+
+        // reverse order in content
+        n1.getNode("child").getProperty("prop").remove();
+        n2.getNode("child").setProperty("prop", "a");
+        testRootNode.save();
+
+        Collections.reverse(expected);
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+    }
+
+    public void testChildAxisMixedTypes() throws RepositoryException {
+        // when differing types are used then the class name of the type
+        // is used for comparison:
+        // java.lang.Double < java.lang.Integer
+        checkChildAxis(new Value[]{getValue(2.0), getValue(1)});
+    }
+
+    public void disabled_testPerformance() throws RepositoryException {
+        createNodes(testRootNode, 10, 4, 0, new NodeCreationCallback() {
+            public void nodeCreated(Node node, int count) throws
+                    RepositoryException {
+                node.addNode("child").setProperty("property", "value" + count);
+                // save once in a while
+                if (count % 1000 == 0) {
+                    superuser.save();
+                    System.out.println("added " + count + " nodes so far.");
+                }
+            }
+        });
+        superuser.save();
+
+        String xpath = testPath + "//*[child/@property] order by child/@property";
+        for (int i = 0; i < 3; i++) {
+            long time = System.currentTimeMillis();
+            Query query = qm.createQuery(xpath, Query.XPATH);
+            ((QueryImpl) query).setLimit(20);
+            query.execute().getNodes().getSize();
+            time = System.currentTimeMillis() - time;
+            System.out.println("executed query in " + time + " ms.");
+        }
+    }
+
+    //------------------------------< helper >----------------------------------
+
+    private Value getValue(String value) throws RepositoryException {
+        return superuser.getValueFactory().createValue(value);
+    }
+
+    private Value getValue(long value) throws RepositoryException {
+        return superuser.getValueFactory().createValue(value);
+    }
+
+    private Value getValue(double value) throws RepositoryException {
+        return superuser.getValueFactory().createValue(value);
+    }
+
+    private Value getValue(boolean value) throws RepositoryException {
+        return superuser.getValueFactory().createValue(value);
+    }
+
+    private Value getValue(Calendar value) throws RepositoryException {
+        return superuser.getValueFactory().createValue(value);
+    }
+
+    private Value getNameValue(String value) throws RepositoryException {
+        return superuser.getValueFactory().createValue(value, PropertyType.NAME);
+    }
+
+    private Value getPathValue(String value) throws RepositoryException {
+        return superuser.getValueFactory().createValue(value, PropertyType.PATH);
+    }
+
+    /**
+     * Checks if order by with a relative path works on the the passed values.
+     * The values are expected to be in ascending order.
+     *
+     * @param values the values in ascending order.
+     * @throws RepositoryException if an error occurs.
+     */
+    private void checkChildAxis(Value[] values) throws RepositoryException {
+        List expected = new ArrayList();
+        for (int i = 0; i < values.length; i++) {
+            Node n = testRootNode.addNode("node" + i);
+            expected.add(n.getPath());
+            n.addNode("child").setProperty("prop", values[i]);
+        }
+        testRootNode.save();
+
+        String xpath = testPath + "/* order by child/@prop";
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+
+        // descending
+        Collections.reverse(expected);
+        xpath = testPath + "/* order by child/@prop descending";
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+
+        // reverse order in content
+        Collections.reverse(Arrays.asList(values));
+        for (int i = 0; i < values.length; i++) {
+            Node child = testRootNode.getNode("node" + i).getNode("child");
+            child.setProperty("prop", values[i]);
+        }
+        testRootNode.save();
+
+        Collections.reverse(expected);
+        assertEquals(expected, collectPaths(executeQuery(xpath)));
+    }
+
+    private static List collectPaths(QueryResult result)
+            throws RepositoryException {
+        List paths = new ArrayList();
+        for (NodeIterator it = result.getNodes(); it.hasNext(); ) {
+            paths.add(it.nextNode().getPath());
+        }
+        return paths;
+    }
+
+    private int createNodes(Node n, int nodesPerLevel, int levels,
+                            int count, NodeCreationCallback callback)
+            throws RepositoryException {
+        levels--;
+        for (int i = 0; i < nodesPerLevel; i++) {
+            Node child = n.addNode("node" + i);
+            count++;
+            callback.nodeCreated(child, count);
+            if (levels > 0) {
+                count = createNodes(child, nodesPerLevel, levels, count, callback);
+            }
+        }
+        return count;
+    }
+
+    private static interface NodeCreationCallback {
+
+        public void nodeCreated(Node node, int count) throws RepositoryException;
+    }
 }



Mime
View raw message