add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / search / TopFieldCollector.java
1 package org.apache.lucene.search;
2
3 /**
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 import java.io.IOException;
21
22 import org.apache.lucene.index.IndexReader;
23 import org.apache.lucene.search.FieldValueHitQueue.Entry;
24 import org.apache.lucene.util.PriorityQueue;
25
26 /**
27  * A {@link Collector} that sorts by {@link SortField} using
28  * {@link FieldComparator}s.
29  * <p/>
30  * See the {@link #create(org.apache.lucene.search.Sort, int, boolean, boolean, boolean, boolean)} method
31  * for instantiating a TopFieldCollector.
32  * 
33  * @lucene.experimental
34  */
35 public abstract class TopFieldCollector extends TopDocsCollector<Entry> {
36   
37   // TODO: one optimization we could do is to pre-fill
38   // the queue with sentinel value that guaranteed to
39   // always compare lower than a real hit; this would
40   // save having to check queueFull on each insert
41
42   /*
43    * Implements a TopFieldCollector over one SortField criteria, without
44    * tracking document scores and maxScore.
45    */
46   private static class OneComparatorNonScoringCollector extends 
47       TopFieldCollector {
48
49     final FieldComparator comparator;
50     final int reverseMul;
51     
52     public OneComparatorNonScoringCollector(FieldValueHitQueue<Entry> queue,
53         int numHits, boolean fillFields) throws IOException {
54       super(queue, numHits, fillFields);
55       comparator = queue.getComparators()[0];
56       reverseMul = queue.getReverseMul()[0];
57     }
58     
59     final void updateBottom(int doc) {
60       // bottom.score is already set to Float.NaN in add().
61       bottom.doc = docBase + doc;
62       bottom = pq.updateTop();
63     }
64
65     @Override
66     public void collect(int doc) throws IOException {
67       ++totalHits;
68       if (queueFull) {
69         if ((reverseMul * comparator.compareBottom(doc)) <= 0) {
70           // since docs are visited in doc Id order, if compare is 0, it means
71           // this document is largest than anything else in the queue, and
72           // therefore not competitive.
73           return;
74         }
75         
76         // This hit is competitive - replace bottom element in queue & adjustTop
77         comparator.copy(bottom.slot, doc);
78         updateBottom(doc);
79         comparator.setBottom(bottom.slot);
80       } else {
81         // Startup transient: queue hasn't gathered numHits yet
82         final int slot = totalHits - 1;
83         // Copy hit into queue
84         comparator.copy(slot, doc);
85         add(slot, doc, Float.NaN);
86         if (queueFull) {
87           comparator.setBottom(bottom.slot);
88         }
89       }
90     }
91     
92     @Override
93     public void setNextReader(IndexReader reader, int docBase) throws IOException {
94       this.docBase = docBase;
95       comparator.setNextReader(reader, docBase);
96     }
97     
98     @Override
99     public void setScorer(Scorer scorer) throws IOException {
100       comparator.setScorer(scorer);
101     }
102     
103   }
104
105   /*
106    * Implements a TopFieldCollector over one SortField criteria, without
107    * tracking document scores and maxScore, and assumes out of orderness in doc
108    * Ids collection.
109    */
110   private static class OutOfOrderOneComparatorNonScoringCollector extends
111       OneComparatorNonScoringCollector {
112
113     public OutOfOrderOneComparatorNonScoringCollector(FieldValueHitQueue<Entry> queue,
114         int numHits, boolean fillFields) throws IOException {
115       super(queue, numHits, fillFields);
116     }
117     
118     @Override
119     public void collect(int doc) throws IOException {
120       ++totalHits;
121       if (queueFull) {
122         // Fastmatch: return if this hit is not competitive
123         final int cmp = reverseMul * comparator.compareBottom(doc);
124         if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.doc)) {
125           return;
126         }
127         
128         // This hit is competitive - replace bottom element in queue & adjustTop
129         comparator.copy(bottom.slot, doc);
130         updateBottom(doc);
131         comparator.setBottom(bottom.slot);
132       } else {
133         // Startup transient: queue hasn't gathered numHits yet
134         final int slot = totalHits - 1;
135         // Copy hit into queue
136         comparator.copy(slot, doc);
137         add(slot, doc, Float.NaN);
138         if (queueFull) {
139           comparator.setBottom(bottom.slot);
140         }
141       }
142     }
143     
144     @Override
145     public boolean acceptsDocsOutOfOrder() {
146       return true;
147     }
148
149   }
150
151   /*
152    * Implements a TopFieldCollector over one SortField criteria, while tracking
153    * document scores but no maxScore.
154    */
155   private static class OneComparatorScoringNoMaxScoreCollector extends
156       OneComparatorNonScoringCollector {
157
158     Scorer scorer;
159
160     public OneComparatorScoringNoMaxScoreCollector(FieldValueHitQueue<Entry> queue,
161         int numHits, boolean fillFields) throws IOException {
162       super(queue, numHits, fillFields);
163     }
164     
165     final void updateBottom(int doc, float score) {
166       bottom.doc = docBase + doc;
167       bottom.score = score;
168       bottom = pq.updateTop();
169     }
170
171     @Override
172     public void collect(int doc) throws IOException {
173       ++totalHits;
174       if (queueFull) {
175         if ((reverseMul * comparator.compareBottom(doc)) <= 0) {
176           // since docs are visited in doc Id order, if compare is 0, it means
177           // this document is largest than anything else in the queue, and
178           // therefore not competitive.
179           return;
180         }
181         
182         // Compute the score only if the hit is competitive.
183         final float score = scorer.score();
184
185         // This hit is competitive - replace bottom element in queue & adjustTop
186         comparator.copy(bottom.slot, doc);
187         updateBottom(doc, score);
188         comparator.setBottom(bottom.slot);
189       } else {
190         // Compute the score only if the hit is competitive.
191         final float score = scorer.score();
192
193         // Startup transient: queue hasn't gathered numHits yet
194         final int slot = totalHits - 1;
195         // Copy hit into queue
196         comparator.copy(slot, doc);
197         add(slot, doc, score);
198         if (queueFull) {
199           comparator.setBottom(bottom.slot);
200         }
201       }
202     }
203     
204     @Override
205     public void setScorer(Scorer scorer) throws IOException {
206       this.scorer = scorer;
207       comparator.setScorer(scorer);
208     }
209     
210   }
211
212   /*
213    * Implements a TopFieldCollector over one SortField criteria, while tracking
214    * document scores but no maxScore, and assumes out of orderness in doc Ids
215    * collection.
216    */
217   private static class OutOfOrderOneComparatorScoringNoMaxScoreCollector extends
218       OneComparatorScoringNoMaxScoreCollector {
219
220     public OutOfOrderOneComparatorScoringNoMaxScoreCollector(
221         FieldValueHitQueue<Entry> queue, int numHits, boolean fillFields)
222         throws IOException {
223       super(queue, numHits, fillFields);
224     }
225     
226     @Override
227     public void collect(int doc) throws IOException {
228       ++totalHits;
229       if (queueFull) {
230         // Fastmatch: return if this hit is not competitive
231         final int cmp = reverseMul * comparator.compareBottom(doc);
232         if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.doc)) {
233           return;
234         }
235         
236         // Compute the score only if the hit is competitive.
237         final float score = scorer.score();
238
239         // This hit is competitive - replace bottom element in queue & adjustTop
240         comparator.copy(bottom.slot, doc);
241         updateBottom(doc, score);
242         comparator.setBottom(bottom.slot);
243       } else {
244         // Compute the score only if the hit is competitive.
245         final float score = scorer.score();
246
247         // Startup transient: queue hasn't gathered numHits yet
248         final int slot = totalHits - 1;
249         // Copy hit into queue
250         comparator.copy(slot, doc);
251         add(slot, doc, score);
252         if (queueFull) {
253           comparator.setBottom(bottom.slot);
254         }
255       }
256     }
257     
258     @Override
259     public boolean acceptsDocsOutOfOrder() {
260       return true;
261     }
262
263   }
264
265   /*
266    * Implements a TopFieldCollector over one SortField criteria, with tracking
267    * document scores and maxScore.
268    */
269   private static class OneComparatorScoringMaxScoreCollector extends
270       OneComparatorNonScoringCollector {
271
272     Scorer scorer;
273     
274     public OneComparatorScoringMaxScoreCollector(FieldValueHitQueue<Entry> queue,
275         int numHits, boolean fillFields) throws IOException {
276       super(queue, numHits, fillFields);
277       // Must set maxScore to NEG_INF, or otherwise Math.max always returns NaN.
278       maxScore = Float.NEGATIVE_INFINITY;
279     }
280     
281     final void updateBottom(int doc, float score) {
282       bottom.doc = docBase + doc;
283       bottom.score = score;
284       bottom =  pq.updateTop();
285     }
286
287     @Override
288     public void collect(int doc) throws IOException {
289       final float score = scorer.score();
290       if (score > maxScore) {
291         maxScore = score;
292       }
293       ++totalHits;
294       if (queueFull) {
295         if ((reverseMul * comparator.compareBottom(doc)) <= 0) {
296           // since docs are visited in doc Id order, if compare is 0, it means
297           // this document is largest than anything else in the queue, and
298           // therefore not competitive.
299           return;
300         }
301         
302         // This hit is competitive - replace bottom element in queue & adjustTop
303         comparator.copy(bottom.slot, doc);
304         updateBottom(doc, score);
305         comparator.setBottom(bottom.slot);
306       } else {
307         // Startup transient: queue hasn't gathered numHits yet
308         final int slot = totalHits - 1;
309         // Copy hit into queue
310         comparator.copy(slot, doc);
311         add(slot, doc, score);
312         if (queueFull) {
313           comparator.setBottom(bottom.slot);
314         }
315       }
316
317     }
318     
319     @Override
320     public void setScorer(Scorer scorer) throws IOException {
321       this.scorer = scorer;
322       super.setScorer(scorer);
323     }
324   }
325
326   /*
327    * Implements a TopFieldCollector over one SortField criteria, with tracking
328    * document scores and maxScore, and assumes out of orderness in doc Ids
329    * collection.
330    */
331   private static class OutOfOrderOneComparatorScoringMaxScoreCollector extends
332       OneComparatorScoringMaxScoreCollector {
333
334     public OutOfOrderOneComparatorScoringMaxScoreCollector(FieldValueHitQueue<Entry> queue,
335         int numHits, boolean fillFields) throws IOException {
336       super(queue, numHits, fillFields);
337     }
338     
339     @Override
340     public void collect(int doc) throws IOException {
341       final float score = scorer.score();
342       if (score > maxScore) {
343         maxScore = score;
344       }
345       ++totalHits;
346       if (queueFull) {
347         // Fastmatch: return if this hit is not competitive
348         final int cmp = reverseMul * comparator.compareBottom(doc);
349         if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.doc)) {
350           return;
351         }
352         
353         // This hit is competitive - replace bottom element in queue & adjustTop
354         comparator.copy(bottom.slot, doc);
355         updateBottom(doc, score);
356         comparator.setBottom(bottom.slot);
357       } else {
358         // Startup transient: queue hasn't gathered numHits yet
359         final int slot = totalHits - 1;
360         // Copy hit into queue
361         comparator.copy(slot, doc);
362         add(slot, doc, score);
363         if (queueFull) {
364           comparator.setBottom(bottom.slot);
365         }
366       }
367     }
368     
369     @Override
370     public boolean acceptsDocsOutOfOrder() {
371       return true;
372     }
373
374   }
375
376   /*
377    * Implements a TopFieldCollector over multiple SortField criteria, without
378    * tracking document scores and maxScore.
379    */
380   private static class MultiComparatorNonScoringCollector extends TopFieldCollector {
381     
382     final FieldComparator[] comparators;
383     final int[] reverseMul;
384
385     public MultiComparatorNonScoringCollector(FieldValueHitQueue<Entry> queue,
386         int numHits, boolean fillFields) throws IOException {
387       super(queue, numHits, fillFields);
388       comparators = queue.getComparators();
389       reverseMul = queue.getReverseMul();
390     }
391     
392     final void updateBottom(int doc) {
393       // bottom.score is already set to Float.NaN in add().
394       bottom.doc = docBase + doc;
395       bottom = pq.updateTop();
396     }
397
398     @Override
399     public void collect(int doc) throws IOException {
400       ++totalHits;
401       if (queueFull) {
402         // Fastmatch: return if this hit is not competitive
403         for (int i = 0;; i++) {
404           final int c = reverseMul[i] * comparators[i].compareBottom(doc);
405           if (c < 0) {
406             // Definitely not competitive.
407             return;
408           } else if (c > 0) {
409             // Definitely competitive.
410             break;
411           } else if (i == comparators.length - 1) {
412             // Here c=0. If we're at the last comparator, this doc is not
413             // competitive, since docs are visited in doc Id order, which means
414             // this doc cannot compete with any other document in the queue.
415             return;
416           }
417         }
418
419         // This hit is competitive - replace bottom element in queue & adjustTop
420         for (int i = 0; i < comparators.length; i++) {
421           comparators[i].copy(bottom.slot, doc);
422         }
423
424         updateBottom(doc);
425
426         for (int i = 0; i < comparators.length; i++) {
427           comparators[i].setBottom(bottom.slot);
428         }
429       } else {
430         // Startup transient: queue hasn't gathered numHits yet
431         final int slot = totalHits - 1;
432         // Copy hit into queue
433         for (int i = 0; i < comparators.length; i++) {
434           comparators[i].copy(slot, doc);
435         }
436         add(slot, doc, Float.NaN);
437         if (queueFull) {
438           for (int i = 0; i < comparators.length; i++) {
439             comparators[i].setBottom(bottom.slot);
440           }
441         }
442       }
443     }
444
445     @Override
446     public void setNextReader(IndexReader reader, int docBase) throws IOException {
447       this.docBase = docBase;
448       for (int i = 0; i < comparators.length; i++) {
449         comparators[i].setNextReader(reader, docBase);
450       }
451     }
452
453     @Override
454     public void setScorer(Scorer scorer) throws IOException {
455       // set the scorer on all comparators
456       for (int i = 0; i < comparators.length; i++) {
457         comparators[i].setScorer(scorer);
458       }
459     }
460   }
461   
462   /*
463    * Implements a TopFieldCollector over multiple SortField criteria, without
464    * tracking document scores and maxScore, and assumes out of orderness in doc
465    * Ids collection.
466    */
467   private static class OutOfOrderMultiComparatorNonScoringCollector extends
468       MultiComparatorNonScoringCollector {
469     
470     public OutOfOrderMultiComparatorNonScoringCollector(FieldValueHitQueue<Entry> queue,
471         int numHits, boolean fillFields) throws IOException {
472       super(queue, numHits, fillFields);
473     }
474     
475     @Override
476     public void collect(int doc) throws IOException {
477       ++totalHits;
478       if (queueFull) {
479         // Fastmatch: return if this hit is not competitive
480         for (int i = 0;; i++) {
481           final int c = reverseMul[i] * comparators[i].compareBottom(doc);
482           if (c < 0) {
483             // Definitely not competitive.
484             return;
485           } else if (c > 0) {
486             // Definitely competitive.
487             break;
488           } else if (i == comparators.length - 1) {
489             // This is the equals case.
490             if (doc + docBase > bottom.doc) {
491               // Definitely not competitive
492               return;
493             }
494             break;
495           }
496         }
497
498         // This hit is competitive - replace bottom element in queue & adjustTop
499         for (int i = 0; i < comparators.length; i++) {
500           comparators[i].copy(bottom.slot, doc);
501         }
502
503         updateBottom(doc);
504
505         for (int i = 0; i < comparators.length; i++) {
506           comparators[i].setBottom(bottom.slot);
507         }
508       } else {
509         // Startup transient: queue hasn't gathered numHits yet
510         final int slot = totalHits - 1;
511         // Copy hit into queue
512         for (int i = 0; i < comparators.length; i++) {
513           comparators[i].copy(slot, doc);
514         }
515         add(slot, doc, Float.NaN);
516         if (queueFull) {
517           for (int i = 0; i < comparators.length; i++) {
518             comparators[i].setBottom(bottom.slot);
519           }
520         }
521       }
522     }
523     
524     @Override
525     public boolean acceptsDocsOutOfOrder() {
526       return true;
527     }
528
529   }
530
531   /*
532    * Implements a TopFieldCollector over multiple SortField criteria, with
533    * tracking document scores and maxScore.
534    */
535   private static class MultiComparatorScoringMaxScoreCollector extends MultiComparatorNonScoringCollector {
536     
537     Scorer scorer;
538     
539     public MultiComparatorScoringMaxScoreCollector(FieldValueHitQueue<Entry> queue,
540         int numHits, boolean fillFields) throws IOException {
541       super(queue, numHits, fillFields);
542       // Must set maxScore to NEG_INF, or otherwise Math.max always returns NaN.
543       maxScore = Float.NEGATIVE_INFINITY;
544     }
545     
546     final void updateBottom(int doc, float score) {
547       bottom.doc = docBase + doc;
548       bottom.score = score;
549       bottom =  pq.updateTop();
550     }
551
552     @Override
553     public void collect(int doc) throws IOException {
554       final float score = scorer.score();
555       if (score > maxScore) {
556         maxScore = score;
557       }
558       ++totalHits;
559       if (queueFull) {
560         // Fastmatch: return if this hit is not competitive
561         for (int i = 0;; i++) {
562           final int c = reverseMul[i] * comparators[i].compareBottom(doc);
563           if (c < 0) {
564             // Definitely not competitive.
565             return;
566           } else if (c > 0) {
567             // Definitely competitive.
568             break;
569           } else if (i == comparators.length - 1) {
570             // Here c=0. If we're at the last comparator, this doc is not
571             // competitive, since docs are visited in doc Id order, which means
572             // this doc cannot compete with any other document in the queue.
573             return;
574           }
575         }
576
577         // This hit is competitive - replace bottom element in queue & adjustTop
578         for (int i = 0; i < comparators.length; i++) {
579           comparators[i].copy(bottom.slot, doc);
580         }
581
582         updateBottom(doc, score);
583
584         for (int i = 0; i < comparators.length; i++) {
585           comparators[i].setBottom(bottom.slot);
586         }
587       } else {
588         // Startup transient: queue hasn't gathered numHits yet
589         final int slot = totalHits - 1;
590         // Copy hit into queue
591         for (int i = 0; i < comparators.length; i++) {
592           comparators[i].copy(slot, doc);
593         }
594         add(slot, doc, score);
595         if (queueFull) {
596           for (int i = 0; i < comparators.length; i++) {
597             comparators[i].setBottom(bottom.slot);
598           }
599         }
600       }
601     }
602
603     @Override
604     public void setScorer(Scorer scorer) throws IOException {
605       this.scorer = scorer;
606       super.setScorer(scorer);
607     }
608   }
609
610   /*
611    * Implements a TopFieldCollector over multiple SortField criteria, with
612    * tracking document scores and maxScore, and assumes out of orderness in doc
613    * Ids collection.
614    */
615   private final static class OutOfOrderMultiComparatorScoringMaxScoreCollector
616       extends MultiComparatorScoringMaxScoreCollector {
617     
618     public OutOfOrderMultiComparatorScoringMaxScoreCollector(FieldValueHitQueue<Entry> queue,
619         int numHits, boolean fillFields) throws IOException {
620       super(queue, numHits, fillFields);
621     }
622     
623     @Override
624     public void collect(int doc) throws IOException {
625       final float score = scorer.score();
626       if (score > maxScore) {
627         maxScore = score;
628       }
629       ++totalHits;
630       if (queueFull) {
631         // Fastmatch: return if this hit is not competitive
632         for (int i = 0;; i++) {
633           final int c = reverseMul[i] * comparators[i].compareBottom(doc);
634           if (c < 0) {
635             // Definitely not competitive.
636             return;
637           } else if (c > 0) {
638             // Definitely competitive.
639             break;
640           } else if (i == comparators.length - 1) {
641             // This is the equals case.
642             if (doc + docBase > bottom.doc) {
643               // Definitely not competitive
644               return;
645             }
646             break;
647           }
648         }
649
650         // This hit is competitive - replace bottom element in queue & adjustTop
651         for (int i = 0; i < comparators.length; i++) {
652           comparators[i].copy(bottom.slot, doc);
653         }
654
655         updateBottom(doc, score);
656
657         for (int i = 0; i < comparators.length; i++) {
658           comparators[i].setBottom(bottom.slot);
659         }
660       } else {
661         // Startup transient: queue hasn't gathered numHits yet
662         final int slot = totalHits - 1;
663         // Copy hit into queue
664         for (int i = 0; i < comparators.length; i++) {
665           comparators[i].copy(slot, doc);
666         }
667         add(slot, doc, score);
668         if (queueFull) {
669           for (int i = 0; i < comparators.length; i++) {
670             comparators[i].setBottom(bottom.slot);
671           }
672         }
673       }
674     }
675     
676     @Override
677     public boolean acceptsDocsOutOfOrder() {
678       return true;
679     }
680
681   }
682
683   /*
684    * Implements a TopFieldCollector over multiple SortField criteria, with
685    * tracking document scores and maxScore.
686    */
687   private static class MultiComparatorScoringNoMaxScoreCollector extends MultiComparatorNonScoringCollector {
688     
689     Scorer scorer;
690     
691     public MultiComparatorScoringNoMaxScoreCollector(FieldValueHitQueue<Entry> queue,
692         int numHits, boolean fillFields) throws IOException {
693       super(queue, numHits, fillFields);
694     }
695     
696     final void updateBottom(int doc, float score) {
697       bottom.doc = docBase + doc;
698       bottom.score = score;
699       bottom = pq.updateTop();
700     }
701
702     @Override
703     public void collect(int doc) throws IOException {
704       ++totalHits;
705       if (queueFull) {
706         // Fastmatch: return if this hit is not competitive
707         for (int i = 0;; i++) {
708           final int c = reverseMul[i] * comparators[i].compareBottom(doc);
709           if (c < 0) {
710             // Definitely not competitive.
711             return;
712           } else if (c > 0) {
713             // Definitely competitive.
714             break;
715           } else if (i == comparators.length - 1) {
716             // Here c=0. If we're at the last comparator, this doc is not
717             // competitive, since docs are visited in doc Id order, which means
718             // this doc cannot compete with any other document in the queue.
719             return;
720           }
721         }
722
723         // This hit is competitive - replace bottom element in queue & adjustTop
724         for (int i = 0; i < comparators.length; i++) {
725           comparators[i].copy(bottom.slot, doc);
726         }
727
728         // Compute score only if it is competitive.
729         final float score = scorer.score();
730         updateBottom(doc, score);
731
732         for (int i = 0; i < comparators.length; i++) {
733           comparators[i].setBottom(bottom.slot);
734         }
735       } else {
736         // Startup transient: queue hasn't gathered numHits yet
737         final int slot = totalHits - 1;
738         // Copy hit into queue
739         for (int i = 0; i < comparators.length; i++) {
740           comparators[i].copy(slot, doc);
741         }
742
743         // Compute score only if it is competitive.
744         final float score = scorer.score();
745         add(slot, doc, score);
746         if (queueFull) {
747           for (int i = 0; i < comparators.length; i++) {
748             comparators[i].setBottom(bottom.slot);
749           }
750         }
751       }
752     }
753
754     @Override
755     public void setScorer(Scorer scorer) throws IOException {
756       this.scorer = scorer;
757       super.setScorer(scorer);
758     }
759   }
760
761   /*
762    * Implements a TopFieldCollector over multiple SortField criteria, with
763    * tracking document scores and maxScore, and assumes out of orderness in doc
764    * Ids collection.
765    */
766   private final static class OutOfOrderMultiComparatorScoringNoMaxScoreCollector
767       extends MultiComparatorScoringNoMaxScoreCollector {
768     
769     public OutOfOrderMultiComparatorScoringNoMaxScoreCollector(
770         FieldValueHitQueue<Entry> queue, int numHits, boolean fillFields)
771         throws IOException {
772       super(queue, numHits, fillFields);
773     }
774     
775     @Override
776     public void collect(int doc) throws IOException {
777       ++totalHits;
778       if (queueFull) {
779         // Fastmatch: return if this hit is not competitive
780         for (int i = 0;; i++) {
781           final int c = reverseMul[i] * comparators[i].compareBottom(doc);
782           if (c < 0) {
783             // Definitely not competitive.
784             return;
785           } else if (c > 0) {
786             // Definitely competitive.
787             break;
788           } else if (i == comparators.length - 1) {
789             // This is the equals case.
790             if (doc + docBase > bottom.doc) {
791               // Definitely not competitive
792               return;
793             }
794             break;
795           }
796         }
797
798         // This hit is competitive - replace bottom element in queue & adjustTop
799         for (int i = 0; i < comparators.length; i++) {
800           comparators[i].copy(bottom.slot, doc);
801         }
802
803         // Compute score only if it is competitive.
804         final float score = scorer.score();
805         updateBottom(doc, score);
806
807         for (int i = 0; i < comparators.length; i++) {
808           comparators[i].setBottom(bottom.slot);
809         }
810       } else {
811         // Startup transient: queue hasn't gathered numHits yet
812         final int slot = totalHits - 1;
813         // Copy hit into queue
814         for (int i = 0; i < comparators.length; i++) {
815           comparators[i].copy(slot, doc);
816         }
817
818         // Compute score only if it is competitive.
819         final float score = scorer.score();
820         add(slot, doc, score);
821         if (queueFull) {
822           for (int i = 0; i < comparators.length; i++) {
823             comparators[i].setBottom(bottom.slot);
824           }
825         }
826       }
827     }
828
829     @Override
830     public void setScorer(Scorer scorer) throws IOException {
831       this.scorer = scorer;
832       super.setScorer(scorer);
833     }
834     
835     @Override
836     public boolean acceptsDocsOutOfOrder() {
837       return true;
838     }
839
840   }
841
842   private static final ScoreDoc[] EMPTY_SCOREDOCS = new ScoreDoc[0];
843   
844   private final boolean fillFields;
845
846   /*
847    * Stores the maximum score value encountered, needed for normalizing. If
848    * document scores are not tracked, this value is initialized to NaN.
849    */
850   float maxScore = Float.NaN;
851
852   final int numHits;
853   FieldValueHitQueue.Entry bottom = null;
854   boolean queueFull;
855   int docBase;
856   
857   // Declaring the constructor private prevents extending this class by anyone
858   // else. Note that the class cannot be final since it's extended by the
859   // internal versions. If someone will define a constructor with any other
860   // visibility, then anyone will be able to extend the class, which is not what
861   // we want.
862   private TopFieldCollector(PriorityQueue<Entry> pq, int numHits, boolean fillFields) {
863     super(pq);
864     this.numHits = numHits;
865     this.fillFields = fillFields;
866   }
867
868   /**
869    * Creates a new {@link TopFieldCollector} from the given
870    * arguments.
871    *
872    * <p><b>NOTE</b>: The instances returned by this method
873    * pre-allocate a full array of length
874    * <code>numHits</code>.
875    * 
876    * @param sort
877    *          the sort criteria (SortFields).
878    * @param numHits
879    *          the number of results to collect.
880    * @param fillFields
881    *          specifies whether the actual field values should be returned on
882    *          the results (FieldDoc).
883    * @param trackDocScores
884    *          specifies whether document scores should be tracked and set on the
885    *          results. Note that if set to false, then the results' scores will
886    *          be set to Float.NaN. Setting this to true affects performance, as
887    *          it incurs the score computation on each competitive result.
888    *          Therefore if document scores are not required by the application,
889    *          it is recommended to set it to false.
890    * @param trackMaxScore
891    *          specifies whether the query's maxScore should be tracked and set
892    *          on the resulting {@link TopDocs}. Note that if set to false,
893    *          {@link TopDocs#getMaxScore()} returns Float.NaN. Setting this to
894    *          true affects performance as it incurs the score computation on
895    *          each result. Also, setting this true automatically sets
896    *          <code>trackDocScores</code> to true as well.
897    * @param docsScoredInOrder
898    *          specifies whether documents are scored in doc Id order or not by
899    *          the given {@link Scorer} in {@link #setScorer(Scorer)}.
900    * @return a {@link TopFieldCollector} instance which will sort the results by
901    *         the sort criteria.
902    * @throws IOException
903    */
904   public static TopFieldCollector create(Sort sort, int numHits,
905       boolean fillFields, boolean trackDocScores, boolean trackMaxScore,
906       boolean docsScoredInOrder)
907       throws IOException {
908     if (sort.fields.length == 0) {
909       throw new IllegalArgumentException("Sort must contain at least one field");
910     }
911     
912     if (numHits <= 0) {
913       throw new IllegalArgumentException("numHits must be > 0; please use TotalHitCountCollector if you just need the total hit count");
914     }
915
916     FieldValueHitQueue<Entry> queue = FieldValueHitQueue.create(sort.fields, numHits);
917     if (queue.getComparators().length == 1) {
918       if (docsScoredInOrder) {
919         if (trackMaxScore) {
920           return new OneComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
921         } else if (trackDocScores) {
922           return new OneComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
923         } else {
924           return new OneComparatorNonScoringCollector(queue, numHits, fillFields);
925         }
926       } else {
927         if (trackMaxScore) {
928           return new OutOfOrderOneComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
929         } else if (trackDocScores) {
930           return new OutOfOrderOneComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
931         } else {
932           return new OutOfOrderOneComparatorNonScoringCollector(queue, numHits, fillFields);
933         }
934       }
935     }
936
937     // multiple comparators.
938     if (docsScoredInOrder) {
939       if (trackMaxScore) {
940         return new MultiComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
941       } else if (trackDocScores) {
942         return new MultiComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
943       } else {
944         return new MultiComparatorNonScoringCollector(queue, numHits, fillFields);
945       }
946     } else {
947       if (trackMaxScore) {
948         return new OutOfOrderMultiComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
949       } else if (trackDocScores) {
950         return new OutOfOrderMultiComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
951       } else {
952         return new OutOfOrderMultiComparatorNonScoringCollector(queue, numHits, fillFields);
953       }
954     }
955   }
956   
957   final void add(int slot, int doc, float score) {
958     bottom = pq.add(new Entry(slot, docBase + doc, score));
959     queueFull = totalHits == numHits;
960   }
961
962   /*
963    * Only the following callback methods need to be overridden since
964    * topDocs(int, int) calls them to return the results.
965    */
966
967   @Override
968   protected void populateResults(ScoreDoc[] results, int howMany) {
969     if (fillFields) {
970       // avoid casting if unnecessary.
971       FieldValueHitQueue<Entry> queue = (FieldValueHitQueue<Entry>) pq;
972       for (int i = howMany - 1; i >= 0; i--) {
973         results[i] = queue.fillFields(queue.pop());
974       }
975     } else {
976       for (int i = howMany - 1; i >= 0; i--) {
977         Entry entry = pq.pop();
978         results[i] = new FieldDoc(entry.doc, entry.score);
979       }
980     }
981   }
982   
983   @Override
984   protected TopDocs newTopDocs(ScoreDoc[] results, int start) {
985     if (results == null) {
986       results = EMPTY_SCOREDOCS;
987       // Set maxScore to NaN, in case this is a maxScore tracking collector.
988       maxScore = Float.NaN;
989     }
990
991     // If this is a maxScoring tracking collector and there were no results, 
992     return new TopFieldDocs(totalHits, results, ((FieldValueHitQueue<Entry>) pq).getFields(), maxScore);
993   }
994   
995   @Override
996   public boolean acceptsDocsOutOfOrder() {
997     return false;
998   }
999 }