2 * Copyright 2005 The Apache Software Foundation
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using PriorityQueue
= Lucene
.Net
.Util
.PriorityQueue
;
20 namespace Lucene
.Net
.Search
23 /// <summary>A Scorer for OR like queries, counterpart of Lucene's <code>ConjunctionScorer</code>.
24 /// This Scorer implements {@link Scorer#SkipTo(int)} and uses skipTo() on the given Scorers.
26 public class DisjunctionSumScorer
: Scorer
28 /// <summary>The number of subscorers. </summary>
29 private int nrScorers
;
31 /// <summary>The subscorers. </summary>
32 protected internal System
.Collections
.IList subScorers
;
34 /// <summary>The minimum number of scorers that should match. </summary>
35 private int minimumNrMatchers
;
37 /// <summary>The scorerQueue contains all subscorers ordered by their current doc(),
38 /// with the minimum at the top.
39 /// <br>The scorerQueue is initialized the first time next() or skipTo() is called.
40 /// <br>An exhausted scorer is immediately removed from the scorerQueue.
41 /// <br>If less than the minimumNrMatchers scorers
42 /// remain in the scorerQueue next() and skipTo() return false.
44 /// After each to call to next() or skipTo()
45 /// <code>currentSumScore</code> is the total score of the current matching doc,
46 /// <code>nrMatchers</code> is the number of matching scorers,
47 /// and all scorers are after the matching doc, or are exhausted.
49 private ScorerQueue scorerQueue
= null;
51 /// <summary>The document number of the current match. </summary>
52 private int currentDoc
= - 1;
54 /// <summary>The number of subscorers that provide the current match. </summary>
55 protected internal int nrMatchers
= - 1;
57 private float currentScore
= System
.Single
.NaN
;
59 /// <summary>Construct a <code>DisjunctionScorer</code>.</summary>
60 /// <param name="subScorers">A collection of at least two subscorers.
62 /// <param name="minimumNrMatchers">The positive minimum number of subscorers that should
63 /// match to match this query.
64 /// <br>When <code>minimumNrMatchers</code> is bigger than
65 /// the number of <code>subScorers</code>,
66 /// no matches will be produced.
67 /// <br>When minimumNrMatchers equals the number of subScorers,
68 /// it more efficient to use <code>ConjunctionScorer</code>.
70 public DisjunctionSumScorer(System
.Collections
.IList subScorers
, int minimumNrMatchers
) : base(null)
73 nrScorers
= subScorers
.Count
;
75 if (minimumNrMatchers
<= 0)
77 throw new System
.ArgumentException("Minimum nr of matchers must be positive");
81 throw new System
.ArgumentException("There must be at least 2 subScorers");
84 this.minimumNrMatchers
= minimumNrMatchers
;
85 this.subScorers
= subScorers
;
88 /// <summary>Construct a <code>DisjunctionScorer</code>, using one as the minimum number
89 /// of matching subscorers.
91 public DisjunctionSumScorer(System
.Collections
.IList subScorers
) : this(subScorers
, 1)
95 /// <summary>Called the first time next() or skipTo() is called to
96 /// initialize <code>scorerQueue</code>.
98 private void InitScorerQueue()
100 System
.Collections
.IEnumerator si
= subScorers
.GetEnumerator();
101 scorerQueue
= new ScorerQueue(this, nrScorers
);
102 while (si
.MoveNext())
104 Scorer se
= (Scorer
) si
.Current
;
107 // doc() method will be used in scorerQueue.
108 scorerQueue
.Insert(se
);
113 /// <summary>A <code>PriorityQueue</code> that orders by {@link Scorer#Doc()}. </summary>
114 private class ScorerQueue
: PriorityQueue
116 private void InitBlock(DisjunctionSumScorer enclosingInstance
)
118 this.enclosingInstance
= enclosingInstance
;
120 private DisjunctionSumScorer enclosingInstance
;
121 public DisjunctionSumScorer Enclosing_Instance
125 return enclosingInstance
;
129 internal ScorerQueue(DisjunctionSumScorer enclosingInstance
, int size
)
131 InitBlock(enclosingInstance
);
135 public override bool LessThan(System
.Object o1
, System
.Object o2
)
137 return ((Scorer
) o1
).Doc() < ((Scorer
) o2
).Doc();
141 public override bool Next()
143 if (scorerQueue
== null)
147 if (scorerQueue
.Size() < minimumNrMatchers
)
153 return AdvanceAfterCurrent();
158 /// <summary>Advance all subscorers after the current document determined by the
159 /// top of the <code>scorerQueue</code>.
160 /// Repeat until at least the minimum number of subscorers match on the same
161 /// document and all subscorers are after that document or are exhausted.
162 /// <br>On entry the <code>scorerQueue</code> has at least <code>minimumNrMatchers</code>
163 /// available. At least the scorer with the minimum document number will be advanced.
165 /// <returns> true iff there is a match.
166 /// <br>In case there is a match, </code>currentDoc</code>, </code>currentSumScore</code>,
167 /// and </code>nrMatchers</code> describe the match.
170 /// <todo> Investigate whether it is possible to use skipTo() when </todo>
171 /// <summary> the minimum number of matchers is bigger than one, ie. try and use the
172 /// character of ConjunctionScorer for the minimum number of matchers.
174 protected internal virtual bool AdvanceAfterCurrent()
178 // repeat until minimum nr of matchers
179 Scorer top
= (Scorer
) scorerQueue
.Top();
180 currentDoc
= top
.Doc();
181 currentScore
= top
.Score();
185 // Until all subscorers are after currentDoc
188 scorerQueue
.AdjustTop();
193 if (scorerQueue
.Size() < (minimumNrMatchers
- nrMatchers
))
195 // Not enough subscorers left for a match on this document,
196 // and also no more chance of any further match.
199 if (scorerQueue
.Size() == 0)
201 break; // nothing more to advance, check for last match.
204 top
= (Scorer
) scorerQueue
.Top();
205 if (top
.Doc() != currentDoc
)
207 break; // All remaining subscorers are after currentDoc.
211 currentScore
+= top
.Score();
217 if (nrMatchers
>= minimumNrMatchers
)
221 else if (scorerQueue
.Size() < minimumNrMatchers
)
229 /// <summary>Returns the score of the current document matching the query.
230 /// Initially invalid, until {@link #Next()} is called the first time.
232 public override float Score()
237 public override int Doc()
242 /// <summary>Returns the number of subscorers matching the current document.
243 /// Initially invalid, until {@link #Next()} is called the first time.
245 public virtual int NrMatchers()
250 /// <summary>Skips to the first match beyond the current whose document number is
251 /// greater than or equal to a given target.
252 /// <br>When this method is used the {@link #Explain(int)} method should not be used.
253 /// <br>The implementation uses the skipTo() method on the subscorers.
255 /// <param name="target">The target document number.
257 /// <returns> true iff there is such a match.
259 public override bool SkipTo(int target
)
261 if (scorerQueue
== null)
265 if (scorerQueue
.Size() < minimumNrMatchers
)
269 if (target
<= currentDoc
)
271 target
= currentDoc
+ 1;
275 Scorer top
= (Scorer
) scorerQueue
.Top();
276 if (top
.Doc() >= target
)
278 return AdvanceAfterCurrent();
280 else if (top
.SkipTo(target
))
282 scorerQueue
.AdjustTop();
287 if (scorerQueue
.Size() < minimumNrMatchers
)
296 /// <summary>Gives and explanation for the score of a given document.</summary>
297 /// <todo> Show the resulting score. See BooleanScorer.explain() on how to do this. </todo>
298 public override Explanation
Explain(int doc
)
300 Explanation res
= new Explanation();
301 res
.SetDescription("At least " + minimumNrMatchers
+ " of");
302 System
.Collections
.IEnumerator ssi
= subScorers
.GetEnumerator();
303 while (ssi
.MoveNext())
305 res
.AddDetail(((Scorer
) ssi
.Current
).Explain(doc
));