2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 using IndexReader = Mono.Lucene.Net.Index.IndexReader;
21 using ByteParser = Mono.Lucene.Net.Search.ByteParser;
22 using DoubleParser = Mono.Lucene.Net.Search.DoubleParser;
23 using FloatParser = Mono.Lucene.Net.Search.FloatParser;
24 using IntParser = Mono.Lucene.Net.Search.IntParser;
25 using LongParser = Mono.Lucene.Net.Search.LongParser;
26 using ShortParser = Mono.Lucene.Net.Search.ShortParser;
27 using StringIndex = Mono.Lucene.Net.Search.StringIndex;
29 namespace Mono.Lucene.Net.Search
32 /// <summary> Expert: a FieldComparator compares hits so as to determine their
33 /// sort order when collecting the top results with {@link
34 /// TopFieldCollector}. The concrete public FieldComparator
35 /// classes here correspond to the SortField types.
37 /// <p/>This API is designed to achieve high performance
38 /// sorting, by exposing a tight interaction with {@link
39 /// FieldValueHitQueue} as it visits hits. Whenever a hit is
40 /// competitive, it's enrolled into a virtual slot, which is
41 /// an int ranging from 0 to numHits-1. The {@link
42 /// FieldComparator} is made aware of segment transitions
43 /// during searching in case any internal state it's tracking
44 /// needs to be recomputed during these transitions.<p/>
46 /// <p/>A comparator must define these functions:<p/>
50 /// <li> {@link #compare} Compare a hit at 'slot a'
51 /// with hit 'slot b'.</li>
53 /// <li> {@link #setBottom} This method is called by
54 /// {@link FieldValueHitQueue} to notify the
55 /// FieldComparator of the current weakest ("bottom")
56 /// slot. Note that this slot may not hold the weakest
57 /// value according to your comparator, in cases where
58 /// your comparator is not the primary one (ie, is only
59 /// used to break ties from the comparators before it).</li>
61 /// <li> {@link #compareBottom} Compare a new hit (docID)
62 /// against the "weakest" (bottom) entry in the queue.</li>
64 /// <li> {@link #copy} Installs a new hit into the
65 /// priority queue. The {@link FieldValueHitQueue}
66 /// calls this method when a new hit is competitive.</li>
68 /// <li> {@link #setNextReader} Invoked
69 /// when the search is switching to the next segment.
70 /// You may need to update internal state of the
71 /// comparator, for example retrieving new values from
72 /// the {@link FieldCache}.</li>
74 /// <li> {@link #value} Return the sort value stored in
75 /// the specified slot. This is only called at the end
76 /// of the search, in order to populate {@link
77 /// FieldDoc#fields} when returning the top results.</li>
80 /// <b>NOTE:</b> This API is experimental and might change in
81 /// incompatible ways in the next release.
83 public abstract class FieldComparator
86 /// <summary>Parses field's values as byte (using {@link
87 /// FieldCache#getBytes} and sorts by ascending value
89 public sealed class ByteComparator:FieldComparator
91 private sbyte[] values;
92 private sbyte[] currentReaderValues;
93 private System.String field;
94 private ByteParser parser;
97 internal ByteComparator(int numHits, System.String field, Mono.Lucene.Net.Search.Parser parser)
99 values = new sbyte[numHits];
101 this.parser = (ByteParser) parser;
104 public override int Compare(int slot1, int slot2)
106 return values[slot1] - values[slot2];
109 public override int CompareBottom(int doc)
111 return bottom - currentReaderValues[doc];
114 public override void Copy(int slot, int doc)
116 values[slot] = currentReaderValues[doc];
119 public override void SetNextReader(IndexReader reader, int docBase)
121 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetBytes(reader, field, parser);
124 public override void SetBottom(int bottom)
126 this.bottom = values[bottom];
129 public override System.IComparable Value(int slot)
131 return (sbyte) values[slot];
135 /// <summary>Sorts by ascending docID </summary>
136 public sealed class DocComparator:FieldComparator
138 private int[] docIDs;
142 internal DocComparator(int numHits)
144 docIDs = new int[numHits];
147 public override int Compare(int slot1, int slot2)
149 // No overflow risk because docIDs are non-negative
150 return docIDs[slot1] - docIDs[slot2];
153 public override int CompareBottom(int doc)
155 // No overflow risk because docIDs are non-negative
156 return bottom - (docBase + doc);
159 public override void Copy(int slot, int doc)
161 docIDs[slot] = docBase + doc;
164 public override void SetNextReader(IndexReader reader, int docBase)
166 // TODO: can we "map" our docIDs to the current
167 // reader? saves having to then subtract on every
169 this.docBase = docBase;
172 public override void SetBottom(int bottom)
174 this.bottom = docIDs[bottom];
177 public override System.IComparable Value(int slot)
179 return (System.Int32) docIDs[slot];
183 /// <summary>Parses field's values as double (using {@link
184 /// FieldCache#getDoubles} and sorts by ascending value
186 public sealed class DoubleComparator:FieldComparator
188 private double[] values;
189 private double[] currentReaderValues;
190 private System.String field;
191 private DoubleParser parser;
192 private double bottom;
194 internal DoubleComparator(int numHits, System.String field, Mono.Lucene.Net.Search.Parser parser)
196 values = new double[numHits];
198 this.parser = (DoubleParser) parser;
201 public override int Compare(int slot1, int slot2)
203 double v1 = values[slot1];
204 double v2 = values[slot2];
219 public override int CompareBottom(int doc)
221 double v2 = currentReaderValues[doc];
226 else if (bottom < v2)
236 public override void Copy(int slot, int doc)
238 values[slot] = currentReaderValues[doc];
241 public override void SetNextReader(IndexReader reader, int docBase)
243 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetDoubles(reader, field, parser);
246 public override void SetBottom(int bottom)
248 this.bottom = values[bottom];
251 public override System.IComparable Value(int slot)
253 return (double) values[slot];
257 /// <summary>Parses field's values as float (using {@link
258 /// FieldCache#getFloats} and sorts by ascending value
260 public sealed class FloatComparator:FieldComparator
262 private float[] values;
263 private float[] currentReaderValues;
264 private System.String field;
265 private FloatParser parser;
266 private float bottom;
268 internal FloatComparator(int numHits, System.String field, Mono.Lucene.Net.Search.Parser parser)
270 values = new float[numHits];
272 this.parser = (FloatParser) parser;
275 public override int Compare(int slot1, int slot2)
277 // TODO: are there sneaky non-branch ways to compute
279 float v1 = values[slot1];
280 float v2 = values[slot2];
295 public override int CompareBottom(int doc)
297 // TODO: are there sneaky non-branch ways to compute
299 float v2 = currentReaderValues[doc];
304 else if (bottom < v2)
314 public override void Copy(int slot, int doc)
316 values[slot] = currentReaderValues[doc];
319 public override void SetNextReader(IndexReader reader, int docBase)
321 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetFloats(reader, field, parser);
324 public override void SetBottom(int bottom)
326 this.bottom = values[bottom];
329 public override System.IComparable Value(int slot)
331 return (float) values[slot];
335 /// <summary>Parses field's values as int (using {@link
336 /// FieldCache#getInts} and sorts by ascending value
338 public sealed class IntComparator:FieldComparator
340 private int[] values;
341 private int[] currentReaderValues;
342 private System.String field;
343 private IntParser parser;
344 private int bottom; // Value of bottom of queue
346 internal IntComparator(int numHits, System.String field, Mono.Lucene.Net.Search.Parser parser)
348 values = new int[numHits];
350 this.parser = (IntParser) parser;
353 public override int Compare(int slot1, int slot2)
355 // TODO: there are sneaky non-branch ways to compute
357 // Cannot return values[slot1] - values[slot2] because that
359 int v1 = values[slot1];
360 int v2 = values[slot2];
375 public override int CompareBottom(int doc)
377 // TODO: there are sneaky non-branch ways to compute
379 // Cannot return bottom - values[slot2] because that
381 int v2 = currentReaderValues[doc];
386 else if (bottom < v2)
396 public override void Copy(int slot, int doc)
398 values[slot] = currentReaderValues[doc];
401 public override void SetNextReader(IndexReader reader, int docBase)
403 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetInts(reader, field, parser);
406 public override void SetBottom(int bottom)
408 this.bottom = values[bottom];
411 public override System.IComparable Value(int slot)
413 return (System.Int32) values[slot];
417 /// <summary>Parses field's values as long (using {@link
418 /// FieldCache#getLongs} and sorts by ascending value
420 public sealed class LongComparator:FieldComparator
422 private long[] values;
423 private long[] currentReaderValues;
424 private System.String field;
425 private LongParser parser;
428 internal LongComparator(int numHits, System.String field, Mono.Lucene.Net.Search.Parser parser)
430 values = new long[numHits];
432 this.parser = (LongParser) parser;
435 public override int Compare(int slot1, int slot2)
437 // TODO: there are sneaky non-branch ways to compute
439 long v1 = values[slot1];
440 long v2 = values[slot2];
455 public override int CompareBottom(int doc)
457 // TODO: there are sneaky non-branch ways to compute
459 long v2 = currentReaderValues[doc];
464 else if (bottom < v2)
474 public override void Copy(int slot, int doc)
476 values[slot] = currentReaderValues[doc];
479 public override void SetNextReader(IndexReader reader, int docBase)
481 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetLongs(reader, field, parser);
484 public override void SetBottom(int bottom)
486 this.bottom = values[bottom];
489 public override System.IComparable Value(int slot)
491 return (long) values[slot];
495 /// <summary>Sorts by descending relevance. NOTE: if you are
496 /// sorting only by descending relevance and then
497 /// secondarily by ascending docID, peformance is faster
498 /// using {@link TopScoreDocCollector} directly (which {@link
499 /// IndexSearcher#search} uses when no {@link Sort} is
502 public sealed class RelevanceComparator:FieldComparator
504 private float[] scores;
505 private float bottom;
506 private Scorer scorer;
508 internal RelevanceComparator(int numHits)
510 scores = new float[numHits];
513 public override int Compare(int slot1, int slot2)
515 float score1 = scores[slot1];
516 float score2 = scores[slot2];
517 return score1 > score2?- 1:(score1 < score2?1:0);
520 public override int CompareBottom(int doc)
522 float score = scorer.Score();
523 return bottom > score?- 1:(bottom < score?1:0);
526 public override void Copy(int slot, int doc)
528 scores[slot] = scorer.Score();
531 public override void SetNextReader(IndexReader reader, int docBase)
535 public override void SetBottom(int bottom)
537 this.bottom = scores[bottom];
540 public override void SetScorer(Scorer scorer)
542 // wrap with a ScoreCachingWrappingScorer so that successive calls to
543 // score() will not incur score computation over and over again.
544 this.scorer = new ScoreCachingWrappingScorer(scorer);
547 public override System.IComparable Value(int slot)
549 return (float) scores[slot];
553 /// <summary>Parses field's values as short (using {@link
554 /// FieldCache#getShorts} and sorts by ascending value
556 public sealed class ShortComparator:FieldComparator
558 private short[] values;
559 private short[] currentReaderValues;
560 private System.String field;
561 private ShortParser parser;
562 private short bottom;
564 internal ShortComparator(int numHits, System.String field, Mono.Lucene.Net.Search.Parser parser)
566 values = new short[numHits];
568 this.parser = (ShortParser) parser;
571 public override int Compare(int slot1, int slot2)
573 return values[slot1] - values[slot2];
576 public override int CompareBottom(int doc)
578 return bottom - currentReaderValues[doc];
581 public override void Copy(int slot, int doc)
583 values[slot] = currentReaderValues[doc];
586 public override void SetNextReader(IndexReader reader, int docBase)
588 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetShorts(reader, field, parser);
591 public override void SetBottom(int bottom)
593 this.bottom = values[bottom];
596 public override System.IComparable Value(int slot)
598 return (short) values[slot];
602 /// <summary>Sorts by a field's value using the Collator for a
605 public sealed class StringComparatorLocale:FieldComparator
608 private System.String[] values;
609 private System.String[] currentReaderValues;
610 private System.String field;
611 internal System.Globalization.CompareInfo collator;
612 private System.String bottom;
614 internal StringComparatorLocale(int numHits, System.String field, System.Globalization.CultureInfo locale)
616 values = new System.String[numHits];
618 collator = locale.CompareInfo;
621 public override int Compare(int slot1, int slot2)
623 System.String val1 = values[slot1];
624 System.String val2 = values[slot2];
633 else if (val2 == null)
637 return collator.Compare(val1.ToString(), val2.ToString());
640 public override int CompareBottom(int doc)
642 System.String val2 = currentReaderValues[doc];
651 else if (val2 == null)
655 return collator.Compare(bottom.ToString(), val2.ToString());
658 public override void Copy(int slot, int doc)
660 values[slot] = currentReaderValues[doc];
663 public override void SetNextReader(IndexReader reader, int docBase)
665 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetStrings(reader, field);
668 public override void SetBottom(int bottom)
670 this.bottom = values[bottom];
673 public override System.IComparable Value(int slot)
679 /// <summary>Sorts by field's natural String sort order, using
680 /// ordinals. This is functionally equivalent to {@link
681 /// StringValComparator}, but it first resolves the string
682 /// to their relative ordinal positions (using the index
683 /// returned by {@link FieldCache#getStringIndex}), and
684 /// does most comparisons using the ordinals. For medium
685 /// to large results, this comparator will be much faster
686 /// than {@link StringValComparator}. For very small
687 /// result sets it may be slower.
689 public sealed class StringOrdValComparator:FieldComparator
693 private System.String[] values;
694 private int[] readerGen;
696 private int currentReaderGen = - 1;
697 private System.String[] lookup;
699 private System.String field;
701 private int bottomSlot = - 1;
702 private int bottomOrd;
703 private System.String bottomValue;
704 private bool reversed;
707 public StringOrdValComparator(int numHits, System.String field, int sortPos, bool reversed)
709 ords = new int[numHits];
710 values = new System.String[numHits];
711 readerGen = new int[numHits];
712 this.sortPos = sortPos;
713 this.reversed = reversed;
717 public override int Compare(int slot1, int slot2)
719 if (readerGen[slot1] == readerGen[slot2])
721 int cmp = ords[slot1] - ords[slot2];
728 System.String val1 = values[slot1];
729 System.String val2 = values[slot2];
738 else if (val2 == null)
742 return String.CompareOrdinal(val1, val2);
745 public override int CompareBottom(int doc)
747 System.Diagnostics.Debug.Assert(bottomSlot != - 1);
748 int order = this.order[doc];
749 int cmp = bottomOrd - order;
755 System.String val2 = lookup[order];
756 if (bottomValue == null)
765 else if (val2 == null)
770 return String.CompareOrdinal(bottomValue, val2);
773 private void Convert(int slot)
775 readerGen[slot] = currentReaderGen;
777 System.String value_Renamed = values[slot];
778 if (value_Renamed == null)
784 if (sortPos == 0 && bottomSlot != - 1 && bottomSlot != slot)
786 // Since we are the primary sort, the entries in the
787 // queue are bounded by bottomOrd:
788 System.Diagnostics.Debug.Assert(bottomOrd < lookup.Length);
791 index = BinarySearch(lookup, value_Renamed, bottomOrd, lookup.Length - 1);
795 index = BinarySearch(lookup, value_Renamed, 0, bottomOrd);
800 // Full binary search
801 index = BinarySearch(lookup, value_Renamed);
811 public override void Copy(int slot, int doc)
813 int ord = order[doc];
815 System.Diagnostics.Debug.Assert(ord >= 0);
816 values[slot] = lookup[ord];
817 readerGen[slot] = currentReaderGen;
820 public override void SetNextReader(IndexReader reader, int docBase)
822 StringIndex currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetStringIndex(reader, field);
824 order = currentReaderValues.order;
825 lookup = currentReaderValues.lookup;
826 System.Diagnostics.Debug.Assert(lookup.Length > 0);
827 if (bottomSlot != - 1)
830 bottomOrd = ords[bottomSlot];
834 public override void SetBottom(int bottom)
837 if (readerGen[bottom] != currentReaderGen)
841 bottomOrd = ords[bottom];
842 System.Diagnostics.Debug.Assert(bottomOrd >= 0);
843 System.Diagnostics.Debug.Assert(bottomOrd < lookup.Length);
844 bottomValue = values[bottom];
847 public override System.IComparable Value(int slot)
852 public System.String[] GetValues()
857 public int GetBottomSlot()
862 public System.String GetField()
868 /// <summary>Sorts by field's natural String sort order. All
869 /// comparisons are done using String.compareTo, which is
870 /// slow for medium to large result sets but possibly
871 /// very fast for very small results sets.
873 public sealed class StringValComparator:FieldComparator
876 private System.String[] values;
877 private System.String[] currentReaderValues;
878 private System.String field;
879 private System.String bottom;
881 internal StringValComparator(int numHits, System.String field)
883 values = new System.String[numHits];
887 public override int Compare(int slot1, int slot2)
889 System.String val1 = values[slot1];
890 System.String val2 = values[slot2];
899 else if (val2 == null)
904 return String.CompareOrdinal(val1, val2);
907 public override int CompareBottom(int doc)
909 System.String val2 = currentReaderValues[doc];
918 else if (val2 == null)
922 return String.CompareOrdinal(bottom, val2);
925 public override void Copy(int slot, int doc)
927 values[slot] = currentReaderValues[doc];
930 public override void SetNextReader(IndexReader reader, int docBase)
932 currentReaderValues = Mono.Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetStrings(reader, field);
935 public override void SetBottom(int bottom)
937 this.bottom = values[bottom];
940 public override System.IComparable Value(int slot)
946 protected internal static int BinarySearch(System.String[] a, System.String key)
948 return BinarySearch(a, key, 0, a.Length - 1);
951 protected internal static int BinarySearch(System.String[] a, System.String key, int low, int high)
956 int mid = SupportClass.Number.URShift((low + high), 1);
957 System.String midVal = a[mid];
961 cmp = String.CompareOrdinal(midVal, key);
978 /// <summary> Compare hit at slot1 with hit at slot2.
981 /// <param name="slot1">first slot to compare
983 /// <param name="slot2">second slot to compare
985 /// <returns> any N < 0 if slot2's value is sorted after
986 /// slot1, any N > 0 if the slot2's value is sorted before
987 /// slot1 and 0 if they are equal
989 public abstract int Compare(int slot1, int slot2);
991 /// <summary> Set the bottom slot, ie the "weakest" (sorted last)
992 /// entry in the queue. When {@link #compareBottom} is
993 /// called, you should compare against this slot. This
994 /// will always be called before {@link #compareBottom}.
997 /// <param name="slot">the currently weakest (sorted last) slot in the queue
999 public abstract void SetBottom(int slot);
1001 /// <summary> Compare the bottom of the queue with doc. This will
1002 /// only invoked after setBottom has been called. This
1003 /// should return the same result as {@link
1004 /// #Compare(int,int)}} as if bottom were slot1 and the new
1005 /// document were slot 2.
1007 /// <p/>For a search that hits many results, this method
1008 /// will be the hotspot (invoked by far the most
1009 /// frequently).<p/>
1012 /// <param name="doc">that was hit
1014 /// <returns> any N < 0 if the doc's value is sorted after
1015 /// the bottom entry (not competitive), any N > 0 if the
1016 /// doc's value is sorted before the bottom entry and 0 if
1019 public abstract int CompareBottom(int doc);
1021 /// <summary> This method is called when a new hit is competitive.
1022 /// You should copy any state associated with this document
1023 /// that will be required for future comparisons, into the
1027 /// <param name="slot">which slot to copy the hit to
1029 /// <param name="doc">docID relative to current reader
1031 public abstract void Copy(int slot, int doc);
1033 /// <summary> Set a new Reader. All doc correspond to the current Reader.
1036 /// <param name="reader">current reader
1038 /// <param name="docBase">docBase of this reader
1040 /// <throws> IOException </throws>
1041 /// <throws> IOException </throws>
1042 public abstract void SetNextReader(IndexReader reader, int docBase);
1044 /// <summary>Sets the Scorer to use in case a document's score is
1048 /// <param name="scorer">Scorer instance that you should use to
1049 /// obtain the current hit's score, if necessary.
1051 public virtual void SetScorer(Scorer scorer)
1053 // Empty implementation since most comparators don't need the score. This
1054 // can be overridden by those that need it.
1057 /// <summary> Return the actual value in the slot.
1060 /// <param name="slot">the value
1062 /// <returns> value in this slot upgraded to Comparable
1064 public abstract System.IComparable Value(int slot);