Index: src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractIndex.java (revision 538490) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractIndex.java (working copy) @@ -32,6 +32,8 @@ import java.io.StringReader; import java.util.BitSet; import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; /** * Implements common functionality for a lucene index. @@ -90,6 +92,9 @@ /** The shared IndexReader for all read-only IndexReaders */ private SharedIndexReader sharedReader; + /** Caches all read-only IndexReaders */ + private Map readOnlyReaders = new HashMap(); + /** * The indexing queue. */ @@ -205,7 +210,12 @@ CachingIndexReader cr = new CachingIndexReader(IndexReader.open(getDirectory()), cache); sharedReader = new SharedIndexReader(cr); } - return new ReadOnlyIndexReader(sharedReader, deleted); + ReadOnlyIndexReader cachedReadOnlyIndexReader = (ReadOnlyIndexReader) readOnlyReaders.get(deleted); + if (cachedReadOnlyIndexReader == null) { + cachedReadOnlyIndexReader = new ReadOnlyIndexReader(sharedReader, deleted); + readOnlyReaders.put(deleted, cachedReadOnlyIndexReader); + } + return cachedReadOnlyIndexReader; } /** @@ -317,6 +327,7 @@ sharedReader.close(); sharedReader = null; } + readOnlyReaders.clear(); } /** Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java (revision 538490) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java (working copy) @@ -20,7 +20,6 @@ import org.apache.lucene.index.Term; import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.TermEnum; -import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.SortComparator; import java.io.IOException; @@ -38,6 +37,22 @@ */ class SharedFieldCache { + /** Expert: Stores term text values and document ordering data. */ + public static class StringIndex { + + /** All the term values, in natural order. */ + public final String[] lookup; + + /** Terms indexed by document id. */ + public final String[] terms; + + /** Creates one of these objects */ + public StringIndex(String[] terms, String[] lookup) { + this.terms = terms; + this.lookup = lookup; + } + } + /** * Reference to the single instance of SharedFieldCache. */ @@ -72,7 +87,7 @@ * information. * @throws IOException if an error occurs while reading from the index. */ - public FieldCache.StringIndex getStringIndex(IndexReader reader, + public SharedFieldCache.StringIndex getStringIndex(IndexReader reader, String field, String prefix, SortComparator comparator, @@ -79,9 +94,9 @@ boolean includeLookup) throws IOException { field = field.intern(); - FieldCache.StringIndex ret = lookup(reader, field, prefix, comparator); + SharedFieldCache.StringIndex ret = lookup(reader, field, prefix, comparator); if (ret == null) { - final int[] retArray = new int[reader.maxDoc()]; + final String[] retArray = new String[reader.maxDoc()]; List mterms = null; if (includeLookup) { mterms = new ArrayList(); @@ -95,7 +110,6 @@ if (includeLookup) { mterms.add(null); // for documents with term number 0 } - int t = 1; // current term number try { if (termEnum.term() == null) { @@ -114,10 +128,8 @@ termDocs.seek(termEnum); while (termDocs.next()) { - retArray[termDocs.doc()] = t; + retArray[termDocs.doc()] = term.text().substring(prefix.length()); } - - t++; } while (termEnum.next()); } finally { termDocs.close(); @@ -128,7 +140,7 @@ if (includeLookup) { lookup = (String[]) mterms.toArray(new String[mterms.size()]); } - FieldCache.StringIndex value = new FieldCache.StringIndex(retArray, lookup); + SharedFieldCache.StringIndex value = new SharedFieldCache.StringIndex(retArray, lookup); store(reader, field, prefix, comparator, value); return value; } @@ -138,7 +150,7 @@ /** * See if a StringIndex object is in the cache. */ - FieldCache.StringIndex lookup(IndexReader reader, String field, + SharedFieldCache.StringIndex lookup(IndexReader reader, String field, String prefix, SortComparator comparer) { Key key = new Key(field, prefix, comparer); synchronized (this) { @@ -146,7 +158,7 @@ if (readerCache == null) { return null; } - return (FieldCache.StringIndex) readerCache.get(key); + return (SharedFieldCache.StringIndex) readerCache.get(key); } } @@ -154,7 +166,7 @@ * Put a StringIndex value to cache. */ Object store(IndexReader reader, String field, String prefix, - SortComparator comparer, FieldCache.StringIndex value) { + SortComparator comparer, SharedFieldCache.StringIndex value) { Key key = new Key(field, prefix, comparer); synchronized (this) { HashMap readerCache = (HashMap) cache.get(reader); Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java (revision 538490) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldSortComparator.java (working copy) @@ -16,8 +16,12 @@ */ package org.apache.jackrabbit.core.query.lucene; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.jackrabbit.core.query.lucene.SharedFieldCache.StringIndex; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.ScoreDocComparator; import org.apache.lucene.search.SortComparator; @@ -23,8 +27,6 @@ import org.apache.lucene.search.SortComparator; import org.apache.lucene.search.SortField; -import java.io.IOException; - /** * Implements a SortComparator which knows how to sort on a lucene * field that contains values for multiple properties. @@ -86,40 +88,57 @@ * @param reader the index reader. * @param propertyName the name of the property to sort. * @return a ScoreDocComparator for the + * @throws IOException * @throws IOException */ - public ScoreDocComparator newComparator(final IndexReader reader, String propertyName) - throws IOException { - // get the StringIndex for propertyName - final FieldCache.StringIndex index - = SharedFieldCache.INSTANCE.getStringIndex(reader, field, - FieldNames.createNamedValue(propertyName, ""), - SharedFieldSortComparator.this, - createComparatorValues); + 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) { - final int fi = index.order[i.doc]; - final int fj = index.order[j.doc]; - if (fi < fj) { - return -1; - } else if (fi > fj) { - return 1; - } else { - return 0; - } - } + int idx1 = readerIndex(i.doc); + int idx2 = readerIndex(j.doc); + + String iTerm = indexes[idx1].terms[i.doc - starts[idx1]]; + String jTerm = indexes[idx2].terms[j.doc - starts[idx2]]; + if (iTerm == jTerm) return 0; + if (iTerm == null) return -1; + if (jTerm == null) return 1; + + return iTerm.compareTo(jTerm); + } + /** - * Returns an empty if no lookup table is available otherwise - * the index term for the score doc i. - * - * @param i the score doc. - * @return the sort value if available. - */ + * Returns an empty if no lookup table is available otherwise the + * index term for the score doc i. + * + * @param i + * the score doc. + * @return the sort value if available. + */ public Comparable sortValue(final ScoreDoc i) { - if (index.lookup != null) { - return index.lookup[index.order[i.doc]]; + if (createComparatorValues) { + StringIndex index = indexes[readerIndex(i.doc)]; + return index.terms[i.doc]; } else { // return dummy value return ""; @@ -129,6 +148,34 @@ public int sortType() { return SortField.CUSTOM; } + + /** + * Returns the reader index for document n. + * + * @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; + } + }; } @@ -138,4 +185,23 @@ protected Comparable getComparable(String termtext) { throw new UnsupportedOperationException(); } + + /** + * Checks if reader is of type {@link MultiIndexReader} and if + * that's the case calls this method recursively for each reader within the + * multi index reader; otherwise the reader is simply added to the list. + * + * @param readers the list of index readers. + * @param reader the reader to check. + */ + private void getIndexReaders(List readers, IndexReader reader) { + if (reader instanceof MultiIndexReader) { + IndexReader[] r = ((MultiIndexReader) reader).getIndexReaders(); + for (int i = 0; i < r.length; i++) { + getIndexReaders(readers, r[i]); + } + } else { + readers.add(reader); + } + } }