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 }