Index: lucene/contrib/queries/src/java/org/apache/lucene/search/SlowCollatedStringComparator.java --- lucene/contrib/queries/src/java/org/apache/lucene/search/SlowCollatedStringComparator.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/contrib/queries/src/java/org/apache/lucene/search/SlowCollatedStringComparator.java Fri Jun 10 15:48:44 2011 -0400 @@ -103,4 +103,18 @@ final String s = values[slot]; return s == null ? null : new BytesRef(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + if (first == null) { + if (second == null) { + return 0; + } + return -1; + } else if (second == null) { + return 1; + } else { + return collator.compare((BytesRef) first, (BytesRef) second); + } + } } Index: lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceFieldComparatorSource.java --- lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceFieldComparatorSource.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceFieldComparatorSource.java Fri Jun 10 15:48:44 2011 -0400 @@ -31,94 +31,104 @@ */ public class DistanceFieldComparatorSource extends FieldComparatorSource { - private DistanceFilter distanceFilter; - private DistanceScoreDocLookupComparator dsdlc; + private DistanceFilter distanceFilter; + private DistanceScoreDocLookupComparator dsdlc; - public DistanceFieldComparatorSource(Filter distanceFilter) { + public DistanceFieldComparatorSource(Filter distanceFilter) { + this.distanceFilter = (DistanceFilter) distanceFilter; + } - this.distanceFilter = (DistanceFilter) distanceFilter; + public void cleanUp() { + distanceFilter = null; - } + if (dsdlc != null) { + dsdlc.cleanUp(); + } - public void cleanUp() { - distanceFilter = null; + dsdlc = null; + } - if (dsdlc != null) - dsdlc.cleanUp(); + @Override + public FieldComparator newComparator(String fieldname, int numHits, + int sortPos, boolean reversed) throws IOException { + dsdlc = new DistanceScoreDocLookupComparator(numHits); + return dsdlc; + } - dsdlc = null; - } + private class DistanceScoreDocLookupComparator extends FieldComparator { - @Override - public FieldComparator newComparator(String fieldname, int numHits, - int sortPos, boolean reversed) throws IOException { - dsdlc = new DistanceScoreDocLookupComparator(numHits); - return dsdlc; - } + private double[] values; + private double bottom; + private int offset =0; + + public DistanceScoreDocLookupComparator(int numHits) { + values = new double[numHits]; + return; + } - private class DistanceScoreDocLookupComparator extends FieldComparator { + @Override + public int compare(int slot1, int slot2) { + double a = values[slot1]; + double b = values[slot2]; + if (a > b) + return 1; + if (a < b) + return -1; - private double[] values; - private double bottom; - private int offset =0; - - public DistanceScoreDocLookupComparator(int numHits) { - values = new double[numHits]; - return; - } + return 0; + } - @Override - public int compare(int slot1, int slot2) { - double a = values[slot1]; - double b = values[slot2]; - if (a > b) - return 1; - if (a < b) - return -1; + public void cleanUp() { + distanceFilter = null; + } - return 0; - } + @Override + public int compareBottom(int doc) { + double v2 = distanceFilter.getDistance(doc+ offset); + + if (bottom > v2) { + return 1; + } else if (bottom < v2) { + return -1; + } + return 0; + } - public void cleanUp() { - distanceFilter = null; - } + @Override + public void copy(int slot, int doc) { + values[slot] = distanceFilter.getDistance(doc + offset); + } - @Override - public int compareBottom(int doc) { - double v2 = distanceFilter.getDistance(doc+ offset); - - if (bottom > v2) { - return 1; - } else if (bottom < v2) { - return -1; - } - return 0; - } - - @Override - public void copy(int slot, int doc) { - values[slot] = distanceFilter.getDistance(doc + offset); - } - - @Override - public void setBottom(int slot) { - this.bottom = values[slot]; - - } + @Override + public void setBottom(int slot) { + this.bottom = values[slot]; + } @Override public FieldComparator setNextReader(AtomicReaderContext context) - throws IOException { + throws IOException { // each reader in a segmented base // has an offset based on the maxDocs of previous readers offset = context.docBase; return this; } - @Override - public Comparable value(int slot) { - return values[slot]; - } - } + @Override + public Comparable value(int slot) { + return values[slot]; + } + @Override + public int compare(Comparable first, Comparable second) { + double v1 = ((Double) first).doubleValue(); + double v2 = ((Double) second).doubleValue(); + if (v1 > v2) { + return 1; + } else if (v1 < v2) { + return -1; + } else { + return 0; + } + } + } } Index: lucene/src/java/org/apache/lucene/search/FieldComparator.java --- lucene/src/java/org/apache/lucene/search/FieldComparator.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/FieldComparator.java Fri Jun 10 15:48:44 2011 -0400 @@ -179,7 +179,8 @@ */ public abstract Comparable value(int slot); - + /** Returns -1 if first is less than second. */ + public abstract int compare(Comparable first, Comparable second); public static abstract class NumericComparator extends FieldComparator { protected final CachedArrayCreator creator; @@ -254,6 +255,11 @@ public Comparable value(int slot) { return Byte.valueOf(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + return ((Byte) first) - ((Byte) second); + } } @@ -326,6 +332,19 @@ public Comparable value(int slot) { return Double.valueOf(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + double v1 = ((Double) first).doubleValue(); + double v2 = ((Double) second).doubleValue(); + if (v1 > v2) { + return 1; + } else if (v1 < v2) { + return -1; + } else { + return 0; + } + } } /** Parses field's values as float (using {@link @@ -400,6 +419,19 @@ public Comparable value(int slot) { return Float.valueOf(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + float v1 = ((Float) first).floatValue(); + float v2 = ((Float) second).floatValue(); + if (v1 > v2) { + return 1; + } else if (v1 < v2) { + return -1; + } else { + return 0; + } + } } /** Parses field's values as short (using {@link @@ -456,6 +488,11 @@ public Comparable value(int slot) { return Short.valueOf(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + return ((Short) first) - ((Short) second); + } } /** Parses field's values as int (using {@link @@ -534,6 +571,19 @@ public Comparable value(int slot) { return Integer.valueOf(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + final int v1 = ((Integer) first).intValue(); + final int v2 = ((Integer) second).intValue(); + if (v1 > v2) { + return 1; + } else if (v1 < v2) { + return -1; + } else { + return 0; + } + } } /** Parses field's values as long (using {@link @@ -609,6 +659,19 @@ public Comparable value(int slot) { return Long.valueOf(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + final long v1 = ((Long) first).longValue(); + final long v2 = ((Long) second).longValue(); + if (v1 > v2) { + return 1; + } else if (v1 < v2) { + return -1; + } else { + return 0; + } + } } /** Sorts by descending relevance. NOTE: if you are @@ -665,6 +728,19 @@ public Comparable value(int slot) { return Float.valueOf(scores[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + final float v1 = ((Float) first).floatValue(); + final float v2 = ((Float) second).floatValue(); + if (v1 > v2) { + return -1; + } else if (v1 < v2) { + return 1; + } else { + return 0; + } + } } @@ -714,6 +790,12 @@ public Comparable value(int slot) { return Integer.valueOf(docIDs[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + // No overflow risk because docIDs are non-negative + return ((Integer) first).intValue() - ((Integer) second).intValue(); + } } /** Sorts by field's natural Term sort order, using @@ -812,6 +894,11 @@ public Comparable value(int slot) { return TermOrdValComparator.this.value(slot); } + + @Override + public int compare(Comparable first, Comparable second) { + return ((BytesRef) first).compareTo((BytesRef) second); + } } // Used per-segment when bit width of doc->ord is 8: @@ -1118,6 +1205,11 @@ public Comparable value(int slot) { return values[slot]; } + + @Override + public int compare(Comparable first, Comparable second) { + return ((BytesRef) first).compareTo((BytesRef) second); + } } /** Sorts by field's natural Term sort order. All @@ -1190,6 +1282,11 @@ public Comparable value(int slot) { return values[slot]; } + + @Override + public int compare(Comparable first, Comparable second) { + return ((BytesRef) first).compareTo((BytesRef) second); + } } final protected static int binarySearch(BytesRef br, DocTermsIndex a, BytesRef key) { Index: lucene/src/java/org/apache/lucene/search/IndexSearcher.java --- lucene/src/java/org/apache/lucene/search/IndexSearcher.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/IndexSearcher.java Fri Jun 10 15:48:44 2011 -0400 @@ -443,7 +443,7 @@ * Collector)}.

*/ protected TopFieldDocs search(Weight weight, Filter filter, int nDocs, - Sort sort, boolean fillFields) + Sort sort, boolean fillFields) throws IOException { if (sort == null) throw new NullPointerException(); Index: lucene/src/java/org/apache/lucene/search/SortField.java --- lucene/src/java/org/apache/lucene/search/SortField.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/SortField.java Fri Jun 10 15:48:44 2011 -0400 @@ -82,10 +82,10 @@ public static final int STRING_VAL = 11; /** Represents sorting by document score (relevance). */ - public static final SortField FIELD_SCORE = new SortField (null, SCORE); + public static final SortField FIELD_SCORE = new SortField(null, SCORE); /** Represents sorting by document number (index order). */ - public static final SortField FIELD_DOC = new SortField (null, DOC); + public static final SortField FIELD_DOC = new SortField(null, DOC); private String field; private int type; // defaults to determining type dynamically @@ -102,7 +102,7 @@ * type is SCORE or DOC. * @param type Type of values in the terms. */ - public SortField (String field, int type) { + public SortField(String field, int type) { initFieldType(field, type); } @@ -113,7 +113,7 @@ * @param type Type of values in the terms. * @param reverse True if natural order should be reversed. */ - public SortField (String field, int type, boolean reverse) { + public SortField(String field, int type, boolean reverse) { initFieldType(field, type); this.reverse = reverse; } @@ -131,7 +131,7 @@ * @deprecated (4.0) use EntryCreator version */ @Deprecated - public SortField (String field, FieldCache.Parser parser) { + public SortField(String field, FieldCache.Parser parser) { this(field, parser, false); } @@ -149,7 +149,7 @@ * @deprecated (4.0) use EntryCreator version */ @Deprecated - public SortField (String field, FieldCache.Parser parser, boolean reverse) { + public SortField(String field, FieldCache.Parser parser, boolean reverse) { if (field == null) { throw new IllegalArgumentException("field can only be null when type is SCORE or DOC"); } @@ -216,7 +216,7 @@ * @param field Name of field to sort by; cannot be null. * @param comparator Returns a comparator for sorting hits. */ - public SortField (String field, FieldComparatorSource comparator) { + public SortField(String field, FieldComparatorSource comparator) { initFieldType(field, CUSTOM); this.comparatorSource = comparator; } @@ -226,7 +226,7 @@ * @param comparator Returns a comparator for sorting hits. * @param reverse True if natural order should be reversed. */ - public SortField (String field, FieldComparatorSource comparator, boolean reverse) { + public SortField(String field, FieldComparatorSource comparator, boolean reverse) { initFieldType(field, CUSTOM); this.reverse = reverse; this.comparatorSource = comparator; Index: lucene/src/java/org/apache/lucene/search/TopDocs.java --- lucene/src/java/org/apache/lucene/search/TopDocs.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/java/org/apache/lucene/search/TopDocs.java Fri Jun 10 15:48:44 2011 -0400 @@ -17,6 +17,10 @@ * limitations under the License. */ +import java.io.IOException; + +import org.apache.lucene.util.PriorityQueue; + /** Represents hits returned by {@link * IndexSearcher#search(Query,Filter,int)} and {@link * IndexSearcher#search(Query,int)}. */ @@ -52,4 +56,211 @@ this.scoreDocs = scoreDocs; this.maxScore = maxScore; } + + // Refers to one hit: + private static class ShardRef { + // Which shard (index into shardHits[]): + final int shardIndex; + + // Which hit within the shard: + int hitIndex; + + public ShardRef(int shardIndex) { + this.shardIndex = shardIndex; + } + + @Override + public String toString() { + return "ShardRef(shardIndex=" + shardIndex + " hitIndex=" + hitIndex + ")"; + } + }; + + // Specialized MergeSortQueue that just merges by + // relevance score, descending: + private static class ScoreMergeSortQueue extends PriorityQueue { + final ScoreDoc[][] shardHits; + + public ScoreMergeSortQueue(TopDocs[] shardHits) { + super(shardHits.length); + this.shardHits = new ScoreDoc[shardHits.length][]; + for(int shardIDX=0;shardIDX secondScore) { + return true; + } else { + // Tie break: earlier shard wins + if (first.shardIndex < second.shardIndex) { + return true; + } else if (first.shardIndex > second.shardIndex) { + return false; + } else { + // Tie break in same shard: resolve however the + // shard had resolved it: + assert first.hitIndex != second.hitIndex; + return first.hitIndex < second.hitIndex; + } + } + } + } + + private static class MergeSortQueue extends PriorityQueue { + // These are really FieldDoc instances: + final ScoreDoc[][] shardHits; + final FieldComparator[] comparators; + final int[] reverseMul; + + public MergeSortQueue(Sort sort, TopDocs[] shardHits) throws IOException { + super(shardHits.length); + this.shardHits = new ScoreDoc[shardHits.length][]; + for(int shardIDX=0;shardIDX second.shardIndex) { + //System.out.println(" return tb false"); + return false; + } else { + // Tie break in same shard: resolve however the + // shard had resolved it: + //System.out.println(" return tb " + (first.hitIndex < second.hitIndex)); + assert first.hitIndex != second.hitIndex; + return first.hitIndex < second.hitIndex; + } + } + } + + /** Returned from {@link #merge}, to include the merged + * TopDocs as well as the reference to which original + * TopDocs shard each hit came from. + * + * @lucene.experimental */ + public static class TopDocsAndShards { + + /** Merged hits from all shards */ + public final TopDocs hits; + + /** Parallel array matching hits.scoreDocs */ + public final int[] shardIndex; + + public TopDocsAndShards(TopDocs hits, int[] shardIndex) { + this.hits = hits; + this.shardIndex = shardIndex; + } + } + + /** Returns a new TopDocs, containing topN results across + * the provided TopDocs, sorting by the specified {@link + * Sort}. Each of the TopDocs must have been sorted by + * the same Sort, and sort field values must have been + * filled (ie, fillFields=true must be + * passed to {@link + * TopFieldCollector#create}. + * + *

Pass sort=null to merge sort by score descending. + * + * @lucene.experimental */ + public static TopDocsAndShards merge(Sort sort, int topN, TopDocs[] shardHits) throws IOException { + + final PriorityQueue queue; + if (sort == null) { + queue = new ScoreMergeSortQueue(shardHits); + } else { + queue = new MergeSortQueue(sort, shardHits); + } + + int totalHitCount = 0; + float maxScore = Float.MIN_VALUE; + for(int shardIDX=0;shardIDX 0) { + totalHitCount += shard.totalHits; + queue.add(new ShardRef(shardIDX)); + maxScore = Math.max(maxScore, shard.getMaxScore()); + //System.out.println(" maxScore now " + maxScore + " vs " + shard.getMaxScore()); + } + } + + final ScoreDoc[] hits = new ScoreDoc[Math.min(topN, totalHitCount)]; + final int[] shardIndex = new int[hits.length]; + + int hitUpto = 0; + while(hitUpto < hits.length) { + assert queue.size() > 0; + ShardRef ref = queue.pop(); + hits[hitUpto] = shardHits[ref.shardIndex].scoreDocs[ref.hitIndex++]; + shardIndex[hitUpto] = ref.shardIndex; + + //System.out.println(" hitUpto=" + hitUpto); + //System.out.println(" doc=" + hits[hitUpto].doc + " score=" + hits[hitUpto].score); + + hitUpto++; + + if (ref.hitIndex < shardHits[ref.shardIndex].scoreDocs.length) { + // Not done with this these TopDocs yet: + queue.add(ref); + } + } + + return new TopDocsAndShards(new TopDocs(totalHitCount, hits, maxScore), + shardIndex); + } } Index: lucene/src/test-framework/org/apache/lucene/index/RandomIndexWriter.java --- lucene/src/test-framework/org/apache/lucene/index/RandomIndexWriter.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/test-framework/org/apache/lucene/index/RandomIndexWriter.java Fri Jun 10 15:48:44 2011 -0400 @@ -230,16 +230,24 @@ return getReader(true); } + private boolean doRandomOptimize = true; + + public void setDoRandomOptimize(boolean v) { + doRandomOptimize = v; + } + private void doRandomOptimize() throws IOException { - final int segCount = w.getSegmentCount(); - if (r.nextBoolean() || segCount == 0) { - // full optimize - w.optimize(); - } else { - // partial optimize - final int limit = _TestUtil.nextInt(r, 1, segCount); - w.optimize(limit); - assert w.getSegmentCount() <= limit: "limit=" + limit + " actual=" + w.getSegmentCount(); + if (doRandomOptimize) { + final int segCount = w.getSegmentCount(); + if (r.nextBoolean() || segCount == 0) { + // full optimize + w.optimize(); + } else { + // partial optimize + final int limit = _TestUtil.nextInt(r, 1, segCount); + w.optimize(limit); + assert w.getSegmentCount() <= limit: "limit=" + limit + " actual=" + w.getSegmentCount(); + } } } Index: lucene/src/test-framework/org/apache/lucene/util/LuceneTestCase.java --- lucene/src/test-framework/org/apache/lucene/util/LuceneTestCase.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/test-framework/org/apache/lucene/util/LuceneTestCase.java Fri Jun 10 15:48:44 2011 -0400 @@ -1484,4 +1484,5 @@ @Ignore("just a hack") public final void alwaysIgnoredTestMethod() {} -} + +} \ No newline at end of file Index: lucene/src/test-framework/org/apache/lucene/util/_TestUtil.java --- lucene/src/test-framework/org/apache/lucene/util/_TestUtil.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/test-framework/org/apache/lucene/util/_TestUtil.java Fri Jun 10 15:48:44 2011 -0400 @@ -27,10 +27,10 @@ import java.io.PrintStream; import java.lang.reflect.Method; import java.util.Enumeration; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Random; -import java.util.Map; -import java.util.HashMap; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -46,6 +46,9 @@ import org.apache.lucene.index.TieredMergePolicy; import org.apache.lucene.index.codecs.Codec; import org.apache.lucene.index.codecs.CodecProvider; +import org.apache.lucene.search.FieldDoc; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.junit.Assert; @@ -425,4 +428,24 @@ newName.append(suffix); return new File(directory, newName.toString()); } + + public static void assertEquals(TopDocs expected, TopDocs actual) { + Assert.assertEquals("wrong total hits", expected.totalHits, actual.totalHits); + Assert.assertEquals("wrong maxScore", expected.getMaxScore(), actual.getMaxScore(), 0.0); + Assert.assertEquals("wrong hit count", expected.scoreDocs.length, actual.scoreDocs.length); + for(int hitIDX=0;hitIDX value(int slot) { throw new UnsupportedOperationException(UNSUPPORTED_MSG); } - + + @Override + public int compare(Comparable first, Comparable second) { + throw new UnsupportedOperationException(UNSUPPORTED_MSG); + } + } static final class JustCompileFieldComparatorSource extends FieldComparatorSource { Index: lucene/src/test/org/apache/lucene/search/TestElevationComparator.java --- lucene/src/test/org/apache/lucene/search/TestElevationComparator.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/test/org/apache/lucene/search/TestElevationComparator.java Fri Jun 10 15:48:44 2011 -0400 @@ -187,6 +187,13 @@ public Comparable value(int slot) { return Integer.valueOf(values[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + // values will be small enough that there is no + // overflow concern + return ((Integer) first).intValue() - ((Integer) second).intValue(); + } }; } } Index: lucene/src/test/org/apache/lucene/search/TestSort.java --- lucene/src/test/org/apache/lucene/search/TestSort.java Thu Jun 09 13:10:14 2011 -0400 +++ lucene/src/test/org/apache/lucene/search/TestSort.java Fri Jun 10 15:48:44 2011 -0400 @@ -482,6 +482,7 @@ @Override public int compare(int slot1, int slot2) { + // values are small enough that overflow won't happen return slotValues[slot1] - slotValues[slot2]; } @@ -511,6 +512,12 @@ public Comparable value(int slot) { return Integer.valueOf(slotValues[slot]); } + + @Override + public int compare(Comparable first, Comparable second) { + // values are small enough that overflow won't happen + return ((Integer) first).intValue() - ((Integer) second).intValue(); + } } static class MyFieldComparatorSource extends FieldComparatorSource { Index: lucene/src/test/org/apache/lucene/search/TestTopDocsMerge.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ lucene/src/test/org/apache/lucene/search/TestTopDocsMerge.java Fri Jun 10 15:48:44 2011 -0400 @@ -0,0 +1,225 @@ +package org.apache.lucene.search; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.NumericField; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.lucene.util._TestUtil; + +public class TestTopDocsMerge extends LuceneTestCase { + + private static class ShardSearcher extends IndexSearcher { + private final IndexReader.AtomicReaderContext[] ctx; + + public ShardSearcher(IndexReader.AtomicReaderContext ctx) { + super(ctx.parent); + this.ctx = new IndexReader.AtomicReaderContext[] {ctx}; + } + + public void search(Weight weight, Collector collector) throws IOException { + search(ctx, weight, null, collector); + } + + public TopDocs search(Weight weight, int topN) throws IOException { + return search(ctx, weight, null, topN); + } + } + + public void testSort() throws Exception { + + IndexReader reader = null; + IndexReader[] subReaders = null; + Directory dir = null; + + final int numDocs = atLeast(1000); + //final int numDocs = atLeast(50); + + final String[] tokens = new String[] {"a", "b", "c", "d", "e"}; + + if (VERBOSE) { + System.out.println("TEST: make index"); + } + + // Iterate until we get an index with more than one segment: + while(true) { + + dir = newDirectory(); + final RandomIndexWriter w = new RandomIndexWriter(random, dir); + w.setDoRandomOptimize(false); + + w.w.getConfig().setMaxBufferedDocs(atLeast(100)); + + final String[] content = new String[atLeast(20)]; + + for(int contentIDX=0;contentIDX sortFields = new ArrayList(); + sortFields.add(new SortField("string", SortField.STRING, true)); + sortFields.add(new SortField("string", SortField.STRING, false)); + sortFields.add(new SortField("int", SortField.INT, true)); + sortFields.add(new SortField("int", SortField.INT, false)); + sortFields.add(new SortField("float", SortField.FLOAT, true)); + sortFields.add(new SortField("float", SortField.FLOAT, false)); + sortFields.add(new SortField(null, SortField.SCORE, true)); + sortFields.add(new SortField(null, SortField.SCORE, false)); + sortFields.add(new SortField(null, SortField.DOC, true)); + sortFields.add(new SortField(null, SortField.DOC, false)); + + for(int iter=0;iter<1000*RANDOM_MULTIPLIER;iter++) { + + // TODO: custom FieldComp... + final Query query = new TermQuery(new Term("text", tokens[random.nextInt(tokens.length)])); + + final Sort sort; + if (random.nextInt(10) == 4) { + // Sort by score + sort = null; + } else { + final SortField[] randomSortFields = new SortField[_TestUtil.nextInt(random, 1, 3)]; + for(int sortIDX=0;sortIDX