1 package org.apache.lucene.search;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.io.IOException;
21 import java.text.Collator;
22 import java.util.Locale;
24 import org.apache.lucene.index.IndexReader;
25 import org.apache.lucene.search.FieldCache.DoubleParser;
26 import org.apache.lucene.search.FieldCache.LongParser;
27 import org.apache.lucene.search.FieldCache.ByteParser;
28 import org.apache.lucene.search.FieldCache.FloatParser;
29 import org.apache.lucene.search.FieldCache.IntParser;
30 import org.apache.lucene.search.FieldCache.ShortParser;
31 import org.apache.lucene.search.FieldCache.StringIndex;
32 import org.apache.lucene.util.Bits;
35 * Expert: a FieldComparator compares hits so as to determine their
36 * sort order when collecting the top results with {@link
37 * TopFieldCollector}. The concrete public FieldComparator
38 * classes here correspond to the SortField types.
40 * <p>This API is designed to achieve high performance
41 * sorting, by exposing a tight interaction with {@link
42 * FieldValueHitQueue} as it visits hits. Whenever a hit is
43 * competitive, it's enrolled into a virtual slot, which is
44 * an int ranging from 0 to numHits-1. The {@link
45 * FieldComparator} is made aware of segment transitions
46 * during searching in case any internal state it's tracking
47 * needs to be recomputed during these transitions.</p>
49 * <p>A comparator must define these functions:</p>
53 * <li> {@link #compare} Compare a hit at 'slot a'
56 * <li> {@link #setBottom} This method is called by
57 * {@link FieldValueHitQueue} to notify the
58 * FieldComparator of the current weakest ("bottom")
59 * slot. Note that this slot may not hold the weakest
60 * value according to your comparator, in cases where
61 * your comparator is not the primary one (ie, is only
62 * used to break ties from the comparators before it).
64 * <li> {@link #compareBottom} Compare a new hit (docID)
65 * against the "weakest" (bottom) entry in the queue.
67 * <li> {@link #copy} Installs a new hit into the
68 * priority queue. The {@link FieldValueHitQueue}
69 * calls this method when a new hit is competitive.
71 * <li> {@link #setNextReader} Invoked
72 * when the search is switching to the next segment.
73 * You may need to update internal state of the
74 * comparator, for example retrieving new values from
75 * the {@link FieldCache}.
77 * <li> {@link #value} Return the sort value stored in
78 * the specified slot. This is only called at the end
79 * of the search, in order to populate {@link
80 * FieldDoc#fields} when returning the top results.
83 * @lucene.experimental
85 public abstract class FieldComparator<T> {
88 * Compare hit at slot1 with hit at slot2.
90 * @param slot1 first slot to compare
91 * @param slot2 second slot to compare
92 * @return any N < 0 if slot2's value is sorted after
93 * slot1, any N > 0 if the slot2's value is sorted before
94 * slot1 and 0 if they are equal
96 public abstract int compare(int slot1, int slot2);
99 * Set the bottom slot, ie the "weakest" (sorted last)
100 * entry in the queue. When {@link #compareBottom} is
101 * called, you should compare against this slot. This
102 * will always be called before {@link #compareBottom}.
104 * @param slot the currently weakest (sorted last) slot in the queue
106 public abstract void setBottom(final int slot);
109 * Compare the bottom of the queue with doc. This will
110 * only invoked after setBottom has been called. This
111 * should return the same result as {@link
112 * #compare(int,int)}} as if bottom were slot1 and the new
113 * document were slot 2.
115 * <p>For a search that hits many results, this method
116 * will be the hotspot (invoked by far the most
119 * @param doc that was hit
120 * @return any N < 0 if the doc's value is sorted after
121 * the bottom entry (not competitive), any N > 0 if the
122 * doc's value is sorted before the bottom entry and 0 if
125 public abstract int compareBottom(int doc) throws IOException;
128 * This method is called when a new hit is competitive.
129 * You should copy any state associated with this document
130 * that will be required for future comparisons, into the
133 * @param slot which slot to copy the hit to
134 * @param doc docID relative to current reader
136 public abstract void copy(int slot, int doc) throws IOException;
139 * Set a new Reader. All doc correspond to the current Reader.
141 * @param reader current reader
142 * @param docBase docBase of this reader
143 * @throws IOException
144 * @throws IOException
146 public abstract void setNextReader(IndexReader reader, int docBase) throws IOException;
148 /** Sets the Scorer to use in case a document's score is
151 * @param scorer Scorer instance that you should use to
152 * obtain the current hit's score, if necessary. */
153 public void setScorer(Scorer scorer) {
154 // Empty implementation since most comparators don't need the score. This
155 // can be overridden by those that need it.
159 * Return the actual value in the slot.
161 * @param slot the value
162 * @return value in this slot
164 public abstract T value(int slot);
166 /** Returns -1 if first is less than second. Default
167 * impl to assume the type implements Comparable and
168 * invoke .compareTo; be sure to override this method if
169 * your FieldComparator's type isn't a Comparable or
170 * if your values may sometimes be null */
171 @SuppressWarnings("unchecked")
172 public int compareValues(T first, T second) {
174 if (second == null) {
179 } else if (second == null) {
182 return ((Comparable<T>) first).compareTo(second);
186 public static abstract class NumericComparator<T extends Number> extends FieldComparator<T> {
187 protected final T missingValue;
188 protected final String field;
189 protected Bits docsWithField;
191 public NumericComparator(String field, T missingValue) {
193 this.missingValue = missingValue;
197 public void setNextReader(IndexReader reader, int docBase) throws IOException {
198 if (missingValue != null) {
199 docsWithField = FieldCache.DEFAULT.getDocsWithField(reader, field);
200 // optimization to remove unneeded checks on the bit interface:
201 if (docsWithField instanceof Bits.MatchAllBits) {
202 docsWithField = null;
205 docsWithField = null;
210 /** Parses field's values as byte (using {@link
211 * FieldCache#getBytes} and sorts by ascending value */
212 public static final class ByteComparator extends NumericComparator<Byte> {
213 private final byte[] values;
214 private final ByteParser parser;
215 private byte[] currentReaderValues;
218 ByteComparator(int numHits, String field, FieldCache.Parser parser, Byte missingValue) {
219 super(field, missingValue);
220 values = new byte[numHits];
221 this.parser = (ByteParser) parser;
225 public int compare(int slot1, int slot2) {
226 return values[slot1] - values[slot2];
230 public int compareBottom(int doc) {
231 byte v2 = currentReaderValues[doc];
232 // Test for v2 == 0 to save Bits.get method call for
233 // the common case (doc has value and value is non-zero):
234 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
241 public void copy(int slot, int doc) {
242 byte v2 = currentReaderValues[doc];
243 // Test for v2 == 0 to save Bits.get method call for
244 // the common case (doc has value and value is non-zero):
245 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
252 public void setNextReader(IndexReader reader, int docBase) throws IOException {
253 // NOTE: must do this before calling super otherwise
254 // we compute the docsWithField Bits twice!
255 currentReaderValues = FieldCache.DEFAULT.getBytes(reader, field, parser, missingValue != null);
256 super.setNextReader(reader, docBase);
260 public void setBottom(final int bottom) {
261 this.bottom = values[bottom];
265 public Byte value(int slot) {
266 return Byte.valueOf(values[slot]);
270 /** Sorts by ascending docID */
271 public static final class DocComparator extends FieldComparator<Integer> {
272 private final int[] docIDs;
276 DocComparator(int numHits) {
277 docIDs = new int[numHits];
281 public int compare(int slot1, int slot2) {
282 // No overflow risk because docIDs are non-negative
283 return docIDs[slot1] - docIDs[slot2];
287 public int compareBottom(int doc) {
288 // No overflow risk because docIDs are non-negative
289 return bottom - (docBase + doc);
293 public void copy(int slot, int doc) {
294 docIDs[slot] = docBase + doc;
298 public void setNextReader(IndexReader reader, int docBase) {
299 // TODO: can we "map" our docIDs to the current
300 // reader? saves having to then subtract on every
302 this.docBase = docBase;
306 public void setBottom(final int bottom) {
307 this.bottom = docIDs[bottom];
311 public Integer value(int slot) {
312 return Integer.valueOf(docIDs[slot]);
316 /** Parses field's values as double (using {@link
317 * FieldCache#getDoubles} and sorts by ascending value */
318 public static final class DoubleComparator extends NumericComparator<Double> {
319 private final double[] values;
320 private final DoubleParser parser;
321 private double[] currentReaderValues;
322 private double bottom;
324 DoubleComparator(int numHits, String field, FieldCache.Parser parser, Double missingValue) {
325 super(field, missingValue);
326 values = new double[numHits];
327 this.parser = (DoubleParser) parser;
331 public int compare(int slot1, int slot2) {
332 final double v1 = values[slot1];
333 final double v2 = values[slot2];
336 } else if (v1 < v2) {
344 public int compareBottom(int doc) {
345 double v2 = currentReaderValues[doc];
346 // Test for v2 == 0 to save Bits.get method call for
347 // the common case (doc has value and value is non-zero):
348 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
353 } else if (bottom < v2) {
361 public void copy(int slot, int doc) {
362 double v2 = currentReaderValues[doc];
363 // Test for v2 == 0 to save Bits.get method call for
364 // the common case (doc has value and value is non-zero):
365 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
372 public void setNextReader(IndexReader reader, int docBase) throws IOException {
373 // NOTE: must do this before calling super otherwise
374 // we compute the docsWithField Bits twice!
375 currentReaderValues = FieldCache.DEFAULT.getDoubles(reader, field, parser, missingValue != null);
376 super.setNextReader(reader, docBase);
380 public void setBottom(final int bottom) {
381 this.bottom = values[bottom];
385 public Double value(int slot) {
386 return Double.valueOf(values[slot]);
390 /** Parses field's values as float (using {@link
391 * FieldCache#getFloats} and sorts by ascending value */
392 public static final class FloatComparator extends NumericComparator<Float> {
393 private final float[] values;
394 private final FloatParser parser;
395 private float[] currentReaderValues;
396 private float bottom;
398 FloatComparator(int numHits, String field, FieldCache.Parser parser, Float missingValue) {
399 super(field, missingValue);
400 values = new float[numHits];
401 this.parser = (FloatParser) parser;
405 public int compare(int slot1, int slot2) {
406 // TODO: are there sneaky non-branch ways to compute
408 final float v1 = values[slot1];
409 final float v2 = values[slot2];
412 } else if (v1 < v2) {
420 public int compareBottom(int doc) {
421 // TODO: are there sneaky non-branch ways to compute
423 float v2 = currentReaderValues[doc];
424 // Test for v2 == 0 to save Bits.get method call for
425 // the common case (doc has value and value is non-zero):
426 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
431 } else if (bottom < v2) {
439 public void copy(int slot, int doc) {
440 float v2 = currentReaderValues[doc];
441 // Test for v2 == 0 to save Bits.get method call for
442 // the common case (doc has value and value is non-zero):
443 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
450 public void setNextReader(IndexReader reader, int docBase) throws IOException {
451 // NOTE: must do this before calling super otherwise
452 // we compute the docsWithField Bits twice!
453 currentReaderValues = FieldCache.DEFAULT.getFloats(reader, field, parser, missingValue != null);
454 super.setNextReader(reader, docBase);
458 public void setBottom(final int bottom) {
459 this.bottom = values[bottom];
463 public Float value(int slot) {
464 return Float.valueOf(values[slot]);
468 /** Parses field's values as int (using {@link
469 * FieldCache#getInts} and sorts by ascending value */
470 public static final class IntComparator extends NumericComparator<Integer> {
471 private final int[] values;
472 private final IntParser parser;
473 private int[] currentReaderValues;
474 private int bottom; // Value of bottom of queue
476 IntComparator(int numHits, String field, FieldCache.Parser parser, Integer missingValue) {
477 super(field, missingValue);
478 values = new int[numHits];
479 this.parser = (IntParser) parser;
483 public int compare(int slot1, int slot2) {
484 // TODO: there are sneaky non-branch ways to compute
486 // Cannot return values[slot1] - values[slot2] because that
488 final int v1 = values[slot1];
489 final int v2 = values[slot2];
492 } else if (v1 < v2) {
500 public int compareBottom(int doc) {
501 // TODO: there are sneaky non-branch ways to compute
503 // Cannot return bottom - values[slot2] because that
505 int v2 = currentReaderValues[doc];
506 // Test for v2 == 0 to save Bits.get method call for
507 // the common case (doc has value and value is non-zero):
508 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
513 } else if (bottom < v2) {
521 public void copy(int slot, int doc) {
522 int v2 = currentReaderValues[doc];
523 // Test for v2 == 0 to save Bits.get method call for
524 // the common case (doc has value and value is non-zero):
525 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
532 public void setNextReader(IndexReader reader, int docBase) throws IOException {
533 // NOTE: must do this before calling super otherwise
534 // we compute the docsWithField Bits twice!
535 currentReaderValues = FieldCache.DEFAULT.getInts(reader, field, parser, missingValue != null);
536 super.setNextReader(reader, docBase);
540 public void setBottom(final int bottom) {
541 this.bottom = values[bottom];
545 public Integer value(int slot) {
546 return Integer.valueOf(values[slot]);
550 /** Parses field's values as long (using {@link
551 * FieldCache#getLongs} and sorts by ascending value */
552 public static final class LongComparator extends NumericComparator<Long> {
553 private final long[] values;
554 private final LongParser parser;
555 private long[] currentReaderValues;
558 LongComparator(int numHits, String field, FieldCache.Parser parser, Long missingValue) {
559 super(field, missingValue);
560 values = new long[numHits];
561 this.parser = (LongParser) parser;
565 public int compare(int slot1, int slot2) {
566 // TODO: there are sneaky non-branch ways to compute
568 final long v1 = values[slot1];
569 final long v2 = values[slot2];
572 } else if (v1 < v2) {
580 public int compareBottom(int doc) {
581 // TODO: there are sneaky non-branch ways to compute
583 long v2 = currentReaderValues[doc];
584 // Test for v2 == 0 to save Bits.get method call for
585 // the common case (doc has value and value is non-zero):
586 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
591 } else if (bottom < v2) {
599 public void copy(int slot, int doc) {
600 long v2 = currentReaderValues[doc];
601 // Test for v2 == 0 to save Bits.get method call for
602 // the common case (doc has value and value is non-zero):
603 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
610 public void setNextReader(IndexReader reader, int docBase) throws IOException {
611 // NOTE: must do this before calling super otherwise
612 // we compute the docsWithField Bits twice!
613 currentReaderValues = FieldCache.DEFAULT.getLongs(reader, field, parser, missingValue != null);
614 super.setNextReader(reader, docBase);
618 public void setBottom(final int bottom) {
619 this.bottom = values[bottom];
623 public Long value(int slot) {
624 return Long.valueOf(values[slot]);
628 /** Sorts by descending relevance. NOTE: if you are
629 * sorting only by descending relevance and then
630 * secondarily by ascending docID, performance is faster
631 * using {@link TopScoreDocCollector} directly (which {@link
632 * IndexSearcher#search} uses when no {@link Sort} is
634 public static final class RelevanceComparator extends FieldComparator<Float> {
635 private final float[] scores;
636 private float bottom;
637 private Scorer scorer;
639 RelevanceComparator(int numHits) {
640 scores = new float[numHits];
644 public int compare(int slot1, int slot2) {
645 final float score1 = scores[slot1];
646 final float score2 = scores[slot2];
647 return score1 > score2 ? -1 : (score1 < score2 ? 1 : 0);
651 public int compareBottom(int doc) throws IOException {
652 float score = scorer.score();
653 return bottom > score ? -1 : (bottom < score ? 1 : 0);
657 public void copy(int slot, int doc) throws IOException {
658 scores[slot] = scorer.score();
662 public void setNextReader(IndexReader reader, int docBase) {
666 public void setBottom(final int bottom) {
667 this.bottom = scores[bottom];
671 public void setScorer(Scorer scorer) {
672 // wrap with a ScoreCachingWrappingScorer so that successive calls to
673 // score() will not incur score computation over and
675 if (!(scorer instanceof ScoreCachingWrappingScorer)) {
676 this.scorer = new ScoreCachingWrappingScorer(scorer);
678 this.scorer = scorer;
683 public Float value(int slot) {
684 return Float.valueOf(scores[slot]);
687 // Override because we sort reverse of natural Float order:
689 public int compareValues(Float first, Float second) {
690 // Reversed intentionally because relevance by default
692 return second.compareTo(first);
696 /** Parses field's values as short (using {@link
697 * FieldCache#getShorts} and sorts by ascending value */
698 public static final class ShortComparator extends NumericComparator<Short> {
699 private final short[] values;
700 private final ShortParser parser;
701 private short[] currentReaderValues;
702 private short bottom;
704 ShortComparator(int numHits, String field, FieldCache.Parser parser, Short missingValue) {
705 super(field, missingValue);
706 values = new short[numHits];
707 this.parser = (ShortParser) parser;
711 public int compare(int slot1, int slot2) {
712 return values[slot1] - values[slot2];
716 public int compareBottom(int doc) {
717 short v2 = currentReaderValues[doc];
718 // Test for v2 == 0 to save Bits.get method call for
719 // the common case (doc has value and value is non-zero):
720 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
727 public void copy(int slot, int doc) {
728 short v2 = currentReaderValues[doc];
729 // Test for v2 == 0 to save Bits.get method call for
730 // the common case (doc has value and value is non-zero):
731 if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
738 public void setNextReader(IndexReader reader, int docBase) throws IOException {
739 // NOTE: must do this before calling super otherwise
740 // we compute the docsWithField Bits twice!
741 currentReaderValues = FieldCache.DEFAULT.getShorts(reader, field, parser, missingValue != null);
742 super.setNextReader(reader, docBase);
746 public void setBottom(final int bottom) {
747 this.bottom = values[bottom];
751 public Short value(int slot) {
752 return Short.valueOf(values[slot]);
756 /** Sorts by a field's value using the Collator for a
758 public static final class StringComparatorLocale extends FieldComparator<String> {
760 private final String[] values;
761 private String[] currentReaderValues;
762 private final String field;
763 final Collator collator;
764 private String bottom;
766 StringComparatorLocale(int numHits, String field, Locale locale) {
767 values = new String[numHits];
769 collator = Collator.getInstance(locale);
773 public int compare(int slot1, int slot2) {
774 final String val1 = values[slot1];
775 final String val2 = values[slot2];
781 } else if (val2 == null) {
784 return collator.compare(val1, val2);
788 public int compareBottom(int doc) {
789 final String val2 = currentReaderValues[doc];
790 if (bottom == null) {
795 } else if (val2 == null) {
798 return collator.compare(bottom, val2);
802 public void copy(int slot, int doc) {
803 values[slot] = currentReaderValues[doc];
807 public void setNextReader(IndexReader reader, int docBase) throws IOException {
808 currentReaderValues = FieldCache.DEFAULT.getStrings(reader, field);
812 public void setBottom(final int bottom) {
813 this.bottom = values[bottom];
817 public String value(int slot) {
822 public int compareValues(String val1, String val2) {
828 } else if (val2 == null) {
831 return collator.compare(val1, val2);
835 /** Sorts by field's natural String sort order, using
836 * ordinals. This is functionally equivalent to {@link
837 * StringValComparator}, but it first resolves the string
838 * to their relative ordinal positions (using the index
839 * returned by {@link FieldCache#getStringIndex}), and
840 * does most comparisons using the ordinals. For medium
841 * to large results, this comparator will be much faster
842 * than {@link StringValComparator}. For very small
843 * result sets it may be slower. */
844 public static final class StringOrdValComparator extends FieldComparator<String> {
846 private final int[] ords;
847 private final String[] values;
848 private final int[] readerGen;
850 private int currentReaderGen = -1;
851 private String[] lookup;
853 private final String field;
855 private int bottomSlot = -1;
856 private int bottomOrd;
857 private boolean bottomSameReader;
858 private String bottomValue;
860 public StringOrdValComparator(int numHits, String field, int sortPos, boolean reversed) {
861 ords = new int[numHits];
862 values = new String[numHits];
863 readerGen = new int[numHits];
868 public int compare(int slot1, int slot2) {
869 if (readerGen[slot1] == readerGen[slot2]) {
870 return ords[slot1] - ords[slot2];
873 final String val1 = values[slot1];
874 final String val2 = values[slot2];
880 } else if (val2 == null) {
883 return val1.compareTo(val2);
887 public int compareBottom(int doc) {
888 assert bottomSlot != -1;
889 if (bottomSameReader) {
890 // ord is precisely comparable, even in the equal case
891 return bottomOrd - this.order[doc];
893 // ord is only approx comparable: if they are not
894 // equal, we can use that; if they are equal, we
895 // must fallback to compare by value
896 final int order = this.order[doc];
897 final int cmp = bottomOrd - order;
902 final String val2 = lookup[order];
903 if (bottomValue == null) {
909 } else if (val2 == null) {
913 return bottomValue.compareTo(val2);
918 public void copy(int slot, int doc) {
919 final int ord = order[doc];
922 values[slot] = lookup[ord];
923 readerGen[slot] = currentReaderGen;
927 public void setNextReader(IndexReader reader, int docBase) throws IOException {
928 StringIndex currentReaderValues = FieldCache.DEFAULT.getStringIndex(reader, field);
930 order = currentReaderValues.order;
931 lookup = currentReaderValues.lookup;
932 assert lookup.length > 0;
933 if (bottomSlot != -1) {
934 setBottom(bottomSlot);
939 public void setBottom(final int bottom) {
942 bottomValue = values[bottomSlot];
943 if (currentReaderGen == readerGen[bottomSlot]) {
944 bottomOrd = ords[bottomSlot];
945 bottomSameReader = true;
947 if (bottomValue == null) {
948 ords[bottomSlot] = 0;
950 bottomSameReader = true;
951 readerGen[bottomSlot] = currentReaderGen;
953 final int index = binarySearch(lookup, bottomValue);
955 bottomOrd = -index - 2;
956 bottomSameReader = false;
960 bottomSameReader = true;
961 readerGen[bottomSlot] = currentReaderGen;
962 ords[bottomSlot] = bottomOrd;
969 public String value(int slot) {
974 public int compareValues(String val1, String val2) {
980 } else if (val2 == null) {
983 return val1.compareTo(val2);
986 public String[] getValues() {
990 public int getBottomSlot() {
994 public String getField() {
999 /** Sorts by field's natural String sort order. All
1000 * comparisons are done using String.compareTo, which is
1001 * slow for medium to large result sets but possibly
1002 * very fast for very small results sets. */
1003 public static final class StringValComparator extends FieldComparator<String> {
1005 private String[] values;
1006 private String[] currentReaderValues;
1007 private final String field;
1008 private String bottom;
1010 StringValComparator(int numHits, String field) {
1011 values = new String[numHits];
1016 public int compare(int slot1, int slot2) {
1017 final String val1 = values[slot1];
1018 final String val2 = values[slot2];
1024 } else if (val2 == null) {
1028 return val1.compareTo(val2);
1032 public int compareBottom(int doc) {
1033 final String val2 = currentReaderValues[doc];
1034 if (bottom == null) {
1039 } else if (val2 == null) {
1042 return bottom.compareTo(val2);
1046 public void copy(int slot, int doc) {
1047 values[slot] = currentReaderValues[doc];
1051 public void setNextReader(IndexReader reader, int docBase) throws IOException {
1052 currentReaderValues = FieldCache.DEFAULT.getStrings(reader, field);
1056 public void setBottom(final int bottom) {
1057 this.bottom = values[bottom];
1061 public String value(int slot) {
1062 return values[slot];
1066 public int compareValues(String val1, String val2) {
1072 } else if (val2 == null) {
1075 return val1.compareTo(val2);
1080 final protected static int binarySearch(String[] a, String key) {
1081 return binarySearch(a, key, 0, a.length-1);
1084 final protected static int binarySearch(String[] a, String key, int low, int high) {
1086 while (low <= high) {
1087 int mid = (low + high) >>> 1;
1088 String midVal = a[mid];
1090 if (midVal != null) {
1091 cmp = midVal.compareTo(key);