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 20 using IndexReader = Lucene.Net.Index.IndexReader; 21 22 namespace Lucene.Net.Search 23 { 24 25 /// <summary> A <see cref="Collector" /> implementation that collects the top-scoring hits, 26 /// returning them as a <see cref="TopDocs" />. This is used by <see cref="IndexSearcher" /> to 27 /// implement <see cref="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. 31 /// 32 /// <p/><b>NOTE</b>: The values <see cref="float.NaN" /> and 33 /// <see cref="float.NegativeInfinity" /> are not valid scores. This 34 /// collector will not properly collect hits with such 35 /// scores. 36 /// </summary> 37 public abstract class TopScoreDocCollector : TopDocsCollector<ScoreDoc> 38 { 39 40 // Assumes docs are scored in order. 41 private class InOrderTopScoreDocCollector:TopScoreDocCollector 42 { InOrderTopScoreDocCollector(int numHits)43 internal InOrderTopScoreDocCollector(int numHits):base(numHits) 44 { 45 } 46 Collect(int doc)47 public override void Collect(int doc) 48 { 49 float score = scorer.Score(); 50 51 // This collector cannot handle these scores: 52 System.Diagnostics.Debug.Assert(score != float.NegativeInfinity); 53 System.Diagnostics.Debug.Assert(!float.IsNaN(score)); 54 55 internalTotalHits++; 56 if (score <= pqTop.Score) 57 { 58 // Since docs are returned in-order (i.e., increasing doc Id), a document 59 // with equal score to pqTop.score cannot compete since HitQueue favors 60 // documents with lower doc Ids. Therefore reject those docs too. 61 return ; 62 } 63 pqTop.Doc = doc + docBase; 64 pqTop.Score = score; 65 pqTop = pq.UpdateTop(); 66 } 67 68 public override bool AcceptsDocsOutOfOrder 69 { 70 get { return false; } 71 } 72 } 73 74 // Assumes docs are scored out of order. 75 private class OutOfOrderTopScoreDocCollector:TopScoreDocCollector 76 { OutOfOrderTopScoreDocCollector(int numHits)77 internal OutOfOrderTopScoreDocCollector(int numHits):base(numHits) 78 { 79 } 80 Collect(int doc)81 public override void Collect(int doc) 82 { 83 float score = scorer.Score(); 84 85 // This collector cannot handle NaN 86 System.Diagnostics.Debug.Assert(!float.IsNaN(score)); 87 88 internalTotalHits++; 89 doc += docBase; 90 if (score < pqTop.Score || (score == pqTop.Score && doc > pqTop.Doc)) 91 { 92 return ; 93 } 94 pqTop.Doc = doc; 95 pqTop.Score = score; 96 pqTop = pq.UpdateTop(); 97 } 98 99 public override bool AcceptsDocsOutOfOrder 100 { 101 get { return true; } 102 } 103 } 104 105 /// <summary> Creates a new <see cref="TopScoreDocCollector" /> given the number of hits to 106 /// collect and whether documents are scored in order by the input 107 /// <see cref="Scorer" /> to <see cref="SetScorer(Scorer)" />. 108 /// 109 /// <p/><b>NOTE</b>: The instances returned by this method 110 /// pre-allocate a full array of length 111 /// <c>numHits</c>, and fill the array with sentinel 112 /// objects. 113 /// </summary> Create(int numHits, bool docsScoredInOrder)114 public static TopScoreDocCollector Create(int numHits, bool docsScoredInOrder) 115 { 116 117 if (docsScoredInOrder) 118 { 119 return new InOrderTopScoreDocCollector(numHits); 120 } 121 else 122 { 123 return new OutOfOrderTopScoreDocCollector(numHits); 124 } 125 } 126 127 internal ScoreDoc pqTop; 128 internal int docBase = 0; 129 internal Scorer scorer; 130 131 // prevents instantiation TopScoreDocCollector(int numHits)132 private TopScoreDocCollector(int numHits):base(new HitQueue(numHits, true)) 133 { 134 // HitQueue implements getSentinelObject to return a ScoreDoc, so we know 135 // that at this point top() is already initialized. 136 pqTop = pq.Top(); 137 } 138 NewTopDocs(ScoreDoc[] results, int start)139 public /*protected internal*/ override TopDocs NewTopDocs(ScoreDoc[] results, int start) 140 { 141 if (results == null) 142 { 143 return EMPTY_TOPDOCS; 144 } 145 146 // We need to compute maxScore in order to set it in TopDocs. If start == 0, 147 // it means the largest element is already in results, use its score as 148 // maxScore. Otherwise pop everything else, until the largest element is 149 // extracted and use its score as maxScore. 150 float maxScore = System.Single.NaN; 151 if (start == 0) 152 { 153 maxScore = results[0].Score; 154 } 155 else 156 { 157 for (int i = pq.Size(); i > 1; i--) 158 { 159 pq.Pop(); 160 } 161 maxScore = pq.Pop().Score; 162 } 163 164 return new TopDocs(internalTotalHits, results, maxScore); 165 } 166 SetNextReader(IndexReader reader, int base_Renamed)167 public override void SetNextReader(IndexReader reader, int base_Renamed) 168 { 169 docBase = base_Renamed; 170 } 171 SetScorer(Scorer scorer)172 public override void SetScorer(Scorer scorer) 173 { 174 this.scorer = scorer; 175 } 176 } 177 }