Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / tools / monkeydoc / Lucene.Net / Lucene.Net / Search / BooleanScorer2.cs
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 Mono.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>
31         class BooleanScorer2:Scorer
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.IList Param1, int Param2):base(Param1, Param2)
49                         {
50                                 InitBlock(enclosingInstance);
51                         }
52                         private int lastScoredDoc = - 1;
53                         // Save the score of lastScoredDoc, so that we don't compute it more than
54                         // once in score().
55                         private float lastDocScore = System.Single.NaN;
56                         public override float Score()
57                         {
58                                 int doc = DocID();
59                                 if (doc >= lastScoredDoc)
60                                 {
61                                         if (doc > lastScoredDoc)
62                                         {
63                                                 lastDocScore = base.Score();
64                                                 lastScoredDoc = doc;
65                                         }
66                                         Enclosing_Instance.coordinator.nrMatchers += base.nrMatchers;
67                                 }
68                                 return lastDocScore;
69                         }
70                 }
71                 private class AnonymousClassConjunctionScorer:ConjunctionScorer
72                 {
73                         private void  InitBlock(int requiredNrMatchers, BooleanScorer2 enclosingInstance)
74                         {
75                                 this.requiredNrMatchers = requiredNrMatchers;
76                                 this.enclosingInstance = enclosingInstance;
77                         }
78                         private int requiredNrMatchers;
79                         private BooleanScorer2 enclosingInstance;
80                         public BooleanScorer2 Enclosing_Instance
81                         {
82                                 get
83                                 {
84                                         return enclosingInstance;
85                                 }
86                                 
87                         }
88                         internal AnonymousClassConjunctionScorer(int requiredNrMatchers, BooleanScorer2 enclosingInstance, Mono.Lucene.Net.Search.Similarity Param1, System.Collections.ICollection Param2):base(Param1, Param2)
89                         {
90                                 InitBlock(requiredNrMatchers, enclosingInstance);
91                         }
92                         private int lastScoredDoc = - 1;
93                         // Save the score of lastScoredDoc, so that we don't compute it more than
94                         // once in score().
95                         private float lastDocScore = System.Single.NaN;
96                         public override float Score()
97                         {
98                                 int doc = DocID();
99                                 if (doc >= lastScoredDoc)
100                                 {
101                                         if (doc > lastScoredDoc)
102                                         {
103                                                 lastDocScore = base.Score();
104                                                 lastScoredDoc = doc;
105                                         }
106                                         Enclosing_Instance.coordinator.nrMatchers += requiredNrMatchers;
107                                 }
108                                 // All scorers match, so defaultSimilarity super.score() always has 1 as
109                                 // the coordination factor.
110                                 // Therefore the sum of the scores of the requiredScorers
111                                 // is used as score.
112                                 return lastDocScore;
113                         }
114                 }
115                 
116                 private System.Collections.IList requiredScorers;
117                 private System.Collections.IList optionalScorers;
118                 private System.Collections.IList prohibitedScorers;
119                 
120                 private class Coordinator
121                 {
122                         public Coordinator(BooleanScorer2 enclosingInstance)
123                         {
124                                 InitBlock(enclosingInstance);
125                         }
126                         private void  InitBlock(BooleanScorer2 enclosingInstance)
127                         {
128                                 this.enclosingInstance = enclosingInstance;
129                         }
130                         private BooleanScorer2 enclosingInstance;
131                         public BooleanScorer2 Enclosing_Instance
132                         {
133                                 get
134                                 {
135                                         return enclosingInstance;
136                                 }
137                                 
138                         }
139                         internal float[] coordFactors = null;
140                         internal int maxCoord = 0; // to be increased for each non prohibited scorer
141                         internal int nrMatchers; // to be increased by score() of match counting scorers.
142                         
143                         internal virtual void  Init()
144                         {
145                                 // use after all scorers have been added.
146                                 coordFactors = new float[maxCoord + 1];
147                                 Similarity sim = Enclosing_Instance.GetSimilarity();
148                                 for (int i = 0; i <= maxCoord; i++)
149                                 {
150                                         coordFactors[i] = sim.Coord(i, maxCoord);
151                                 }
152                         }
153                 }
154                 
155                 private Coordinator coordinator;
156                 
157                 /// <summary>The scorer to which all scoring will be delegated,
158                 /// except for computing and using the coordination factor.
159                 /// </summary>
160                 private Scorer countingSumScorer;
161                 
162                 /// <summary>The number of optionalScorers that need to match (if there are any) </summary>
163                 private int minNrShouldMatch;
164                 
165                 private int doc = - 1;
166                 
167                 /// <summary> Creates a {@link Scorer} with the given similarity and lists of required,
168                 /// prohibited and optional scorers. In no required scorers are added, at least
169                 /// one of the optional scorers will have to match during the search.
170                 /// 
171                 /// </summary>
172                 /// <param name="similarity">The similarity to be used.
173                 /// </param>
174                 /// <param name="minNrShouldMatch">The minimum number of optional added scorers that should match
175                 /// during the search. In case no required scorers are added, at least
176                 /// one of the optional scorers will have to match during the search.
177                 /// </param>
178                 /// <param name="required">the list of required scorers.
179                 /// </param>
180                 /// <param name="prohibited">the list of prohibited scorers.
181                 /// </param>
182                 /// <param name="optional">the list of optional scorers.
183                 /// </param>
184                 public BooleanScorer2(Similarity similarity, int minNrShouldMatch, System.Collections.IList required, System.Collections.IList prohibited, System.Collections.IList optional):base(similarity)
185                 {
186                         if (minNrShouldMatch < 0)
187                         {
188                                 throw new System.ArgumentException("Minimum number of optional scorers should not be negative");
189                         }
190                         coordinator = new Coordinator(this);
191                         this.minNrShouldMatch = minNrShouldMatch;
192                         
193                         optionalScorers = optional;
194                         coordinator.maxCoord += optional.Count;
195                         
196                         requiredScorers = required;
197                         coordinator.maxCoord += required.Count;
198                         
199                         prohibitedScorers = prohibited;
200                         
201                         coordinator.Init();
202                         countingSumScorer = MakeCountingSumScorer();
203                 }
204                 
205                 /// <summary>Count a scorer as a single match. </summary>
206                 private class SingleMatchScorer:Scorer
207                 {
208                         private void  InitBlock(BooleanScorer2 enclosingInstance)
209                         {
210                                 this.enclosingInstance = enclosingInstance;
211                         }
212                         private BooleanScorer2 enclosingInstance;
213                         public BooleanScorer2 Enclosing_Instance
214                         {
215                                 get
216                                 {
217                                         return enclosingInstance;
218                                 }
219                                 
220                         }
221                         private Scorer scorer;
222                         private int lastScoredDoc = - 1;
223                         // Save the score of lastScoredDoc, so that we don't compute it more than
224                         // once in score().
225                         private float lastDocScore = System.Single.NaN;
226                         
227                         internal SingleMatchScorer(BooleanScorer2 enclosingInstance, Scorer scorer):base(scorer.GetSimilarity())
228                         {
229                                 InitBlock(enclosingInstance);
230                                 this.scorer = scorer;
231                         }
232                         public override float Score()
233                         {
234                                 int doc = DocID();
235                                 if (doc >= lastScoredDoc)
236                                 {
237                                         if (doc > lastScoredDoc)
238                                         {
239                                                 lastDocScore = scorer.Score();
240                                                 lastScoredDoc = doc;
241                                         }
242                                         Enclosing_Instance.coordinator.nrMatchers++;
243                                 }
244                                 return lastDocScore;
245                         }
246                         /// <deprecated> use {@link #DocID()} instead. 
247                         /// </deprecated>
248             [Obsolete("use DocID() instead. ")]
249                         public override int Doc()
250                         {
251                                 return scorer.Doc();
252                         }
253                         public override int DocID()
254                         {
255                                 return scorer.DocID();
256                         }
257                         /// <deprecated> use {@link #NextDoc()} instead. 
258                         /// </deprecated>
259             [Obsolete("use NextDoc() instead. ")]
260                         public override bool Next()
261                         {
262                                 return scorer.NextDoc() != NO_MORE_DOCS;
263                         }
264                         public override int NextDoc()
265                         {
266                                 return scorer.NextDoc();
267                         }
268                         /// <deprecated> use {@link #Advance(int)} instead. 
269                         /// </deprecated>
270             [Obsolete("use Advance(int) instead. ")]
271                         public override bool SkipTo(int docNr)
272                         {
273                                 return scorer.Advance(docNr) != NO_MORE_DOCS;
274                         }
275                         public override int Advance(int target)
276                         {
277                                 return scorer.Advance(target);
278                         }
279                         public override Explanation Explain(int docNr)
280                         {
281                                 return scorer.Explain(docNr);
282                         }
283                 }
284                 
285                 private Scorer CountingDisjunctionSumScorer(System.Collections.IList scorers, int minNrShouldMatch)
286                 {
287                         // each scorer from the list counted as a single matcher
288                         return new AnonymousClassDisjunctionSumScorer(this, scorers, minNrShouldMatch);
289                 }
290                 
291                 private static readonly Similarity defaultSimilarity;
292                 
293                 private Scorer CountingConjunctionSumScorer(System.Collections.IList requiredScorers)
294                 {
295                         // each scorer from the list counted as a single matcher
296                         int requiredNrMatchers = requiredScorers.Count;
297                         return new AnonymousClassConjunctionScorer(requiredNrMatchers, this, defaultSimilarity, requiredScorers);
298                 }
299                 
300                 private Scorer DualConjunctionSumScorer(Scorer req1, Scorer req2)
301                 {
302                         // non counting.
303                         return new ConjunctionScorer(defaultSimilarity, new Scorer[]{req1, req2});
304                         // All scorers match, so defaultSimilarity always has 1 as
305                         // the coordination factor.
306                         // Therefore the sum of the scores of two scorers
307                         // is used as score.
308                 }
309                 
310                 /// <summary>Returns the scorer to be used for match counting and score summing.
311                 /// Uses requiredScorers, optionalScorers and prohibitedScorers.
312                 /// </summary>
313                 private Scorer MakeCountingSumScorer()
314                 {
315                         // each scorer counted as a single matcher
316                         return (requiredScorers.Count == 0)?MakeCountingSumScorerNoReq():MakeCountingSumScorerSomeReq();
317                 }
318                 
319                 private Scorer MakeCountingSumScorerNoReq()
320                 {
321                         // No required scorers
322                         // minNrShouldMatch optional scorers are required, but at least 1
323                         int nrOptRequired = (minNrShouldMatch < 1)?1:minNrShouldMatch;
324                         Scorer requiredCountingSumScorer;
325                         if (optionalScorers.Count > nrOptRequired)
326                                 requiredCountingSumScorer = CountingDisjunctionSumScorer(optionalScorers, nrOptRequired);
327                         else if (optionalScorers.Count == 1)
328                                 requiredCountingSumScorer = new SingleMatchScorer(this, (Scorer) optionalScorers[0]);
329                         else
330                                 requiredCountingSumScorer = CountingConjunctionSumScorer(optionalScorers);
331                         return AddProhibitedScorers(requiredCountingSumScorer);
332                 }
333                 
334                 private Scorer MakeCountingSumScorerSomeReq()
335                 {
336                         // At least one required scorer.
337                         if (optionalScorers.Count == minNrShouldMatch)
338                         {
339                                 // all optional scorers also required.
340                                 System.Collections.ArrayList allReq = new System.Collections.ArrayList(requiredScorers);
341                                 allReq.AddRange(optionalScorers);
342                                 return AddProhibitedScorers(CountingConjunctionSumScorer(allReq));
343                         }
344                         else
345                         {
346                                 // optionalScorers.size() > minNrShouldMatch, and at least one required scorer
347                                 Scorer requiredCountingSumScorer = requiredScorers.Count == 1?new SingleMatchScorer(this, (Scorer) requiredScorers[0]):CountingConjunctionSumScorer(requiredScorers);
348                                 if (minNrShouldMatch > 0)
349                                 {
350                                         // use a required disjunction scorer over the optional scorers
351                                         return AddProhibitedScorers(DualConjunctionSumScorer(requiredCountingSumScorer, CountingDisjunctionSumScorer(optionalScorers, minNrShouldMatch)));
352                                 }
353                                 else
354                                 {
355                                         // minNrShouldMatch == 0
356                                         return new ReqOptSumScorer(AddProhibitedScorers(requiredCountingSumScorer), optionalScorers.Count == 1?new SingleMatchScorer(this, (Scorer) optionalScorers[0]):CountingDisjunctionSumScorer(optionalScorers, 1));
357                                 }
358                         }
359                 }
360                 
361                 /// <summary>Returns the scorer to be used for match counting and score summing.
362                 /// Uses the given required scorer and the prohibitedScorers.
363                 /// </summary>
364                 /// <param name="requiredCountingSumScorer">A required scorer already built.
365                 /// </param>
366                 private Scorer AddProhibitedScorers(Scorer requiredCountingSumScorer)
367                 {
368                         return (prohibitedScorers.Count == 0)?requiredCountingSumScorer:new ReqExclScorer(requiredCountingSumScorer, ((prohibitedScorers.Count == 1)?(Scorer) prohibitedScorers[0]:new DisjunctionSumScorer(prohibitedScorers)));
369                 }
370                 
371                 /// <summary>Scores and collects all matching documents.</summary>
372                 /// <param name="hc">The collector to which all matching documents are passed through
373                 /// {@link HitCollector#Collect(int, float)}.
374                 /// <br/>When this method is used the {@link #Explain(int)} method should not be used.
375                 /// </param>
376                 /// <deprecated> use {@link #Score(Collector)} instead.
377                 /// </deprecated>
378         [Obsolete("use Score(Collector) instead.")]
379                 public override void  Score(HitCollector hc)
380                 {
381                         Score(new HitCollectorWrapper(hc));
382                 }
383                 
384                 /// <summary>Scores and collects all matching documents.</summary>
385                 /// <param name="collector">The collector to which all matching documents are passed through.
386                 /// <br/>When this method is used the {@link #Explain(int)} method should not be used.
387                 /// </param>
388                 public override void  Score(Collector collector)
389                 {
390                         collector.SetScorer(this);
391                         while ((doc = countingSumScorer.NextDoc()) != NO_MORE_DOCS)
392                         {
393                                 collector.Collect(doc);
394                         }
395                 }
396                 
397                 /// <summary>Expert: Collects matching documents in a range.
398                 /// <br/>Note that {@link #Next()} must be called once before this method is
399                 /// called for the first time.
400                 /// </summary>
401                 /// <param name="hc">The collector to which all matching documents are passed through
402                 /// {@link HitCollector#Collect(int, float)}.
403                 /// </param>
404                 /// <param name="max">Do not score documents past this.
405                 /// </param>
406                 /// <returns> true if more matching documents may remain.
407                 /// </returns>
408                 /// <deprecated> use {@link #Score(Collector, int, int)} instead.
409                 /// </deprecated>
410         [Obsolete("use Score(Collector, int, int) instead.")]
411                 protected internal override bool Score(HitCollector hc, int max)
412                 {
413                         return Score(new HitCollectorWrapper(hc), max, DocID());
414                 }
415                 
416                 public /*protected internal*/ override bool Score(Collector collector, int max, int firstDocID)
417                 {
418                         doc = firstDocID;
419                         collector.SetScorer(this);
420                         while (doc < max)
421                         {
422                                 collector.Collect(doc);
423                                 doc = countingSumScorer.NextDoc();
424                         }
425                         return doc != NO_MORE_DOCS;
426                 }
427                 
428                 /// <deprecated> use {@link #DocID()} instead. 
429                 /// </deprecated>
430         [Obsolete("use DocID() instead. ")]
431                 public override int Doc()
432                 {
433                         return countingSumScorer.Doc();
434                 }
435                 
436                 public override int DocID()
437                 {
438                         return doc;
439                 }
440                 
441                 /// <deprecated> use {@link #NextDoc()} instead. 
442                 /// </deprecated>
443         [Obsolete("use NextDoc() instead. ")]
444                 public override bool Next()
445                 {
446                         return NextDoc() != NO_MORE_DOCS;
447                 }
448                 
449                 public override int NextDoc()
450                 {
451                         return doc = countingSumScorer.NextDoc();
452                 }
453                 
454                 public override float Score()
455                 {
456                         coordinator.nrMatchers = 0;
457                         float sum = countingSumScorer.Score();
458                         return sum * coordinator.coordFactors[coordinator.nrMatchers];
459                 }
460                 
461                 /// <deprecated> use {@link #Advance(int)} instead. 
462                 /// </deprecated>
463         [Obsolete("use Advance(int) instead. ")]
464                 public override bool SkipTo(int target)
465                 {
466                         return Advance(target) != NO_MORE_DOCS;
467                 }
468                 
469                 public override int Advance(int target)
470                 {
471                         return doc = countingSumScorer.Advance(target);
472                 }
473                 
474                 /// <summary>Throws an UnsupportedOperationException.
475                 /// TODO: Implement an explanation of the coordination factor.
476                 /// </summary>
477                 /// <param name="doc">The document number for the explanation.
478                 /// </param>
479                 /// <throws>  UnsupportedOperationException </throws>
480                 public override Explanation Explain(int doc)
481                 {
482                         throw new System.NotSupportedException();
483                         /* How to explain the coordination factor?
484                         initCountingSumScorer();
485                         return countingSumScorer.explain(doc); // misses coord factor. 
486                         */
487                 }
488                 static BooleanScorer2()
489                 {
490                         defaultSimilarity = Similarity.GetDefault();
491                 }
492         }
493 }