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
BooleanScorer2.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 
20 namespace Lucene.Net.Search
21 {
22 
23  /* See the description in BooleanScorer.java, comparing
24  * BooleanScorer & BooleanScorer2 */
25 
26  /// <summary>An alternative to BooleanScorer that also allows a minimum number
27  /// of optional scorers that should match.
28  /// <br/>Implements skipTo(), and has no limitations on the numbers of added scorers.
29  /// <br/>Uses ConjunctionScorer, DisjunctionScorer, ReqOptScorer and ReqExclScorer.
30  /// </summary>
32  {
33  private class AnonymousClassDisjunctionSumScorer:DisjunctionSumScorer
34  {
35  private void InitBlock(BooleanScorer2 enclosingInstance)
36  {
37  this.enclosingInstance = enclosingInstance;
38  }
39  private BooleanScorer2 enclosingInstance;
40  public BooleanScorer2 Enclosing_Instance
41  {
42  get
43  {
44  return enclosingInstance;
45  }
46 
47  }
48  internal AnonymousClassDisjunctionSumScorer(BooleanScorer2 enclosingInstance, System.Collections.Generic.IList<Scorer> scorers, int minNrShouldMatch)
49  : base(scorers, minNrShouldMatch)
50  {
51  InitBlock(enclosingInstance);
52  }
53  private int lastScoredDoc = - 1;
54  // Save the score of lastScoredDoc, so that we don't compute it more than
55  // once in score().
56  private float lastDocScore = System.Single.NaN;
57  public override float Score()
58  {
59  int doc = DocID();
60  if (doc >= lastScoredDoc)
61  {
62  if (doc > lastScoredDoc)
63  {
64  lastDocScore = base.Score();
65  lastScoredDoc = doc;
66  }
67  Enclosing_Instance.coordinator.nrMatchers += base.nrMatchers;
68  }
69  return lastDocScore;
70  }
71  }
72  private class AnonymousClassConjunctionScorer:ConjunctionScorer
73  {
74  private void InitBlock(int requiredNrMatchers, BooleanScorer2 enclosingInstance)
75  {
76  this.requiredNrMatchers = requiredNrMatchers;
77  this.enclosingInstance = enclosingInstance;
78  }
79  private int requiredNrMatchers;
80  private BooleanScorer2 enclosingInstance;
81  public BooleanScorer2 Enclosing_Instance
82  {
83  get
84  {
85  return enclosingInstance;
86  }
87 
88  }
89  internal AnonymousClassConjunctionScorer(int requiredNrMatchers, BooleanScorer2 enclosingInstance, Lucene.Net.Search.Similarity defaultSimilarity, System.Collections.Generic.IList<Scorer> requiredScorers)
90  : base(defaultSimilarity, requiredScorers)
91  {
92  InitBlock(requiredNrMatchers, enclosingInstance);
93  }
94  private int lastScoredDoc = - 1;
95  // Save the score of lastScoredDoc, so that we don't compute it more than
96  // once in score().
97  private float lastDocScore = System.Single.NaN;
98  public override float Score()
99  {
100  int doc = DocID();
101  if (doc >= lastScoredDoc)
102  {
103  if (doc > lastScoredDoc)
104  {
105  lastDocScore = base.Score();
106  lastScoredDoc = doc;
107  }
108  Enclosing_Instance.coordinator.nrMatchers += requiredNrMatchers;
109  }
110  // All scorers match, so defaultSimilarity super.score() always has 1 as
111  // the coordination factor.
112  // Therefore the sum of the scores of the requiredScorers
113  // is used as score.
114  return lastDocScore;
115  }
116  }
117 
118  private System.Collections.Generic.List<Scorer> requiredScorers;
119  private System.Collections.Generic.List<Scorer> optionalScorers;
120  private System.Collections.Generic.List<Scorer> prohibitedScorers;
121 
122  private class Coordinator
123  {
124  public Coordinator(BooleanScorer2 enclosingInstance)
125  {
126  InitBlock(enclosingInstance);
127  }
128  private void InitBlock(BooleanScorer2 enclosingInstance)
129  {
130  this.enclosingInstance = enclosingInstance;
131  }
132  private BooleanScorer2 enclosingInstance;
133  public BooleanScorer2 Enclosing_Instance
134  {
135  get
136  {
137  return enclosingInstance;
138  }
139 
140  }
141  internal float[] coordFactors = null;
142  internal int maxCoord = 0; // to be increased for each non prohibited scorer
143  internal int nrMatchers; // to be increased by score() of match counting scorers.
144 
145  internal virtual void Init()
146  {
147  // use after all scorers have been added.
148  coordFactors = new float[maxCoord + 1];
149  Similarity sim = Enclosing_Instance.Similarity;
150  for (int i = 0; i <= maxCoord; i++)
151  {
152  coordFactors[i] = sim.Coord(i, maxCoord);
153  }
154  }
155  }
156 
157  private Coordinator coordinator;
158 
159  /// <summary>The scorer to which all scoring will be delegated,
160  /// except for computing and using the coordination factor.
161  /// </summary>
162  private Scorer countingSumScorer;
163 
164  /// <summary>The number of optionalScorers that need to match (if there are any) </summary>
165  private int minNrShouldMatch;
166 
167  private int doc = - 1;
168 
169  /// <summary> Creates a <see cref="Scorer" /> with the given similarity and lists of required,
170  /// prohibited and optional scorers. In no required scorers are added, at least
171  /// one of the optional scorers will have to match during the search.
172  ///
173  /// </summary>
174  /// <param name="similarity">The similarity to be used.
175  /// </param>
176  /// <param name="minNrShouldMatch">The minimum number of optional added scorers that should match
177  /// during the search. In case no required scorers are added, at least
178  /// one of the optional scorers will have to match during the search.
179  /// </param>
180  /// <param name="required">the list of required scorers.
181  /// </param>
182  /// <param name="prohibited">the list of prohibited scorers.
183  /// </param>
184  /// <param name="optional">the list of optional scorers.
185  /// </param>
186  public BooleanScorer2(Similarity similarity, int minNrShouldMatch,
187  System.Collections.Generic.List<Scorer> required,
188  System.Collections.Generic.List<Scorer> prohibited,
189  System.Collections.Generic.List<Scorer> optional)
190  : base(similarity)
191  {
192  if (minNrShouldMatch < 0)
193  {
194  throw new System.ArgumentException("Minimum number of optional scorers should not be negative");
195  }
196  coordinator = new Coordinator(this);
197  this.minNrShouldMatch = minNrShouldMatch;
198 
199  optionalScorers = optional;
200  coordinator.maxCoord += optional.Count;
201 
202  requiredScorers = required;
203  coordinator.maxCoord += required.Count;
204 
205  prohibitedScorers = prohibited;
206 
207  coordinator.Init();
208  countingSumScorer = MakeCountingSumScorer();
209  }
210 
211  /// <summary>Count a scorer as a single match. </summary>
212  private class SingleMatchScorer:Scorer
213  {
214  private void InitBlock(BooleanScorer2 enclosingInstance)
215  {
216  this.enclosingInstance = enclosingInstance;
217  }
218  private BooleanScorer2 enclosingInstance;
219  public BooleanScorer2 Enclosing_Instance
220  {
221  get
222  {
223  return enclosingInstance;
224  }
225 
226  }
227  private Scorer scorer;
228  private int lastScoredDoc = - 1;
229  // Save the score of lastScoredDoc, so that we don't compute it more than
230  // once in score().
231  private float lastDocScore = System.Single.NaN;
232 
233  internal SingleMatchScorer(BooleanScorer2 enclosingInstance, Scorer scorer):base(scorer.Similarity)
234  {
235  InitBlock(enclosingInstance);
236  this.scorer = scorer;
237  }
238  public override float Score()
239  {
240  int doc = DocID();
241  if (doc >= lastScoredDoc)
242  {
243  if (doc > lastScoredDoc)
244  {
245  lastDocScore = scorer.Score();
246  lastScoredDoc = doc;
247  }
248  Enclosing_Instance.coordinator.nrMatchers++;
249  }
250  return lastDocScore;
251  }
252 
253  public override int DocID()
254  {
255  return scorer.DocID();
256  }
257 
258  public override int NextDoc()
259  {
260  return scorer.NextDoc();
261  }
262 
263  public override int Advance(int target)
264  {
265  return scorer.Advance(target);
266  }
267  }
268 
269  private Scorer CountingDisjunctionSumScorer(System.Collections.Generic.List<Scorer> scorers, int minNrShouldMatch)
270  {
271  // each scorer from the list counted as a single matcher
272  return new AnonymousClassDisjunctionSumScorer(this, scorers, minNrShouldMatch);
273  }
274 
275  private static readonly Similarity defaultSimilarity;
276 
277  private Scorer CountingConjunctionSumScorer(System.Collections.Generic.List<Scorer> requiredScorers)
278  {
279  // each scorer from the list counted as a single matcher
280  int requiredNrMatchers = requiredScorers.Count;
281  return new AnonymousClassConjunctionScorer(requiredNrMatchers, this, defaultSimilarity, requiredScorers);
282  }
283 
284  private Scorer DualConjunctionSumScorer(Scorer req1, Scorer req2)
285  {
286  // non counting.
287  return new ConjunctionScorer(defaultSimilarity, new Scorer[]{req1, req2});
288  // All scorers match, so defaultSimilarity always has 1 as
289  // the coordination factor.
290  // Therefore the sum of the scores of two scorers
291  // is used as score.
292  }
293 
294  /// <summary>Returns the scorer to be used for match counting and score summing.
295  /// Uses requiredScorers, optionalScorers and prohibitedScorers.
296  /// </summary>
297  private Scorer MakeCountingSumScorer()
298  {
299  // each scorer counted as a single matcher
300  return (requiredScorers.Count == 0)?MakeCountingSumScorerNoReq():MakeCountingSumScorerSomeReq();
301  }
302 
303  private Scorer MakeCountingSumScorerNoReq()
304  {
305  // No required scorers
306  // minNrShouldMatch optional scorers are required, but at least 1
307  int nrOptRequired = (minNrShouldMatch < 1)?1:minNrShouldMatch;
308  Scorer requiredCountingSumScorer;
309  if (optionalScorers.Count > nrOptRequired)
310  requiredCountingSumScorer = CountingDisjunctionSumScorer(optionalScorers, nrOptRequired);
311  else if (optionalScorers.Count == 1)
312  requiredCountingSumScorer = new SingleMatchScorer(this, optionalScorers[0]);
313  else
314  requiredCountingSumScorer = CountingConjunctionSumScorer(optionalScorers);
315  return AddProhibitedScorers(requiredCountingSumScorer);
316  }
317 
318  private Scorer MakeCountingSumScorerSomeReq()
319  {
320  // At least one required scorer.
321  if (optionalScorers.Count == minNrShouldMatch)
322  {
323  // all optional scorers also required.
324  var allReq = new System.Collections.Generic.List<Scorer>(requiredScorers);
325  allReq.AddRange(optionalScorers);
326  return AddProhibitedScorers(CountingConjunctionSumScorer(allReq));
327  }
328  else
329  {
330  // optionalScorers.size() > minNrShouldMatch, and at least one required scorer
331  Scorer requiredCountingSumScorer =
332  requiredScorers.Count == 1
333  ? new SingleMatchScorer(this, requiredScorers[0])
334  : CountingConjunctionSumScorer(requiredScorers);
335  if (minNrShouldMatch > 0)
336  {
337  // use a required disjunction scorer over the optional scorers
338  return AddProhibitedScorers(DualConjunctionSumScorer(requiredCountingSumScorer, CountingDisjunctionSumScorer(optionalScorers, minNrShouldMatch)));
339  }
340  else
341  {
342  // minNrShouldMatch == 0
343  return new ReqOptSumScorer(AddProhibitedScorers(requiredCountingSumScorer),
344  optionalScorers.Count == 1
345  ? new SingleMatchScorer(this, optionalScorers[0])
346  : CountingDisjunctionSumScorer(optionalScorers, 1));
347  }
348  }
349  }
350 
351  /// <summary>Returns the scorer to be used for match counting and score summing.
352  /// Uses the given required scorer and the prohibitedScorers.
353  /// </summary>
354  /// <param name="requiredCountingSumScorer">A required scorer already built.
355  /// </param>
356  private Scorer AddProhibitedScorers(Scorer requiredCountingSumScorer)
357  {
358  return (prohibitedScorers.Count == 0)
359  ? requiredCountingSumScorer
360  : new ReqExclScorer(requiredCountingSumScorer,
361  ((prohibitedScorers.Count == 1)
362  ? prohibitedScorers[0]
363  : new DisjunctionSumScorer(prohibitedScorers)));
364  }
365 
366  /// <summary>Scores and collects all matching documents.</summary>
367  /// <param name="collector">The collector to which all matching documents are passed through.
368  /// </param>
369  public override void Score(Collector collector)
370  {
371  collector.SetScorer(this);
372  while ((doc = countingSumScorer.NextDoc()) != NO_MORE_DOCS)
373  {
374  collector.Collect(doc);
375  }
376  }
377 
378  public /*protected internal*/ override bool Score(Collector collector, int max, int firstDocID)
379  {
380  doc = firstDocID;
381  collector.SetScorer(this);
382  while (doc < max)
383  {
384  collector.Collect(doc);
385  doc = countingSumScorer.NextDoc();
386  }
387  return doc != NO_MORE_DOCS;
388  }
389 
390  public override int DocID()
391  {
392  return doc;
393  }
394 
395  public override int NextDoc()
396  {
397  return doc = countingSumScorer.NextDoc();
398  }
399 
400  public override float Score()
401  {
402  coordinator.nrMatchers = 0;
403  float sum = countingSumScorer.Score();
404  return sum * coordinator.coordFactors[coordinator.nrMatchers];
405  }
406 
407  public override int Advance(int target)
408  {
409  return doc = countingSumScorer.Advance(target);
410  }
411 
412  static BooleanScorer2()
413  {
414  defaultSimilarity = Search.Similarity.Default;
415  }
416  }
417 }