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