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;
22 import org.apache.lucene.index.IndexReader;
25 * A {@link Collector} implementation that collects the top-scoring hits,
26 * returning them as a {@link TopDocs}. This is used by {@link IndexSearcher} to
27 * implement {@link TopDocs}-based search. Hits are sorted by score descending
28 * and then (when the scores are tied) docID ascending. When you create an
29 * instance of this collector you should know in advance whether documents are
30 * going to be collected in doc Id order or not.
32 * <p><b>NOTE</b>: The values {@link Float#NaN} and
33 * {Float#NEGATIVE_INFINITY} are not valid scores. This
34 * collector will not properly collect hits with such
37 public abstract class TopScoreDocCollector extends TopDocsCollector<ScoreDoc> {
39 // Assumes docs are scored in order.
40 private static class InOrderTopScoreDocCollector extends TopScoreDocCollector {
41 private InOrderTopScoreDocCollector(int numHits) {
46 public void collect(int doc) throws IOException {
47 float score = scorer.score();
49 // This collector cannot handle these scores:
50 assert score != Float.NEGATIVE_INFINITY;
51 assert !Float.isNaN(score);
54 if (score <= pqTop.score) {
55 // Since docs are returned in-order (i.e., increasing doc Id), a document
56 // with equal score to pqTop.score cannot compete since HitQueue favors
57 // documents with lower doc Ids. Therefore reject those docs too.
60 pqTop.doc = doc + docBase;
62 pqTop = pq.updateTop();
66 public boolean acceptsDocsOutOfOrder() {
71 // Assumes docs are scored out of order.
72 private static class OutOfOrderTopScoreDocCollector extends TopScoreDocCollector {
73 private OutOfOrderTopScoreDocCollector(int numHits) {
78 public void collect(int doc) throws IOException {
79 float score = scorer.score();
81 // This collector cannot handle NaN
82 assert !Float.isNaN(score);
85 if (score < pqTop.score) {
86 // Doesn't compete w/ bottom entry in queue
90 if (score == pqTop.score && doc > pqTop.doc) {
91 // Break tie in score by doc ID:
96 pqTop = pq.updateTop();
100 public boolean acceptsDocsOutOfOrder() {
106 * Creates a new {@link TopScoreDocCollector} given the number of hits to
107 * collect and whether documents are scored in order by the input
108 * {@link Scorer} to {@link #setScorer(Scorer)}.
110 * <p><b>NOTE</b>: The instances returned by this method
111 * pre-allocate a full array of length
112 * <code>numHits</code>, and fill the array with sentinel
115 public static TopScoreDocCollector create(int numHits, boolean docsScoredInOrder) {
118 throw new IllegalArgumentException("numHits must be > 0; please use TotalHitCountCollector if you just need the total hit count");
121 if (docsScoredInOrder) {
122 return new InOrderTopScoreDocCollector(numHits);
124 return new OutOfOrderTopScoreDocCollector(numHits);
133 // prevents instantiation
134 private TopScoreDocCollector(int numHits) {
135 super(new HitQueue(numHits, true));
136 // HitQueue implements getSentinelObject to return a ScoreDoc, so we know
137 // that at this point top() is already initialized.
142 protected TopDocs newTopDocs(ScoreDoc[] results, int start) {
143 if (results == null) {
144 return EMPTY_TOPDOCS;
147 // We need to compute maxScore in order to set it in TopDocs. If start == 0,
148 // it means the largest element is already in results, use its score as
149 // maxScore. Otherwise pop everything else, until the largest element is
150 // extracted and use its score as maxScore.
151 float maxScore = Float.NaN;
153 maxScore = results[0].score;
155 for (int i = pq.size(); i > 1; i--) { pq.pop(); }
156 maxScore = pq.pop().score;
159 return new TopDocs(totalHits, results, maxScore);
163 public void setNextReader(IndexReader reader, int base) {
168 public void setScorer(Scorer scorer) throws IOException {
169 this.scorer = scorer;