KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > lucene > search > BooleanScorer2


1 package org.apache.lucene.search;
2
3 /**
4  * Copyright 2005 The Apache Software Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import java.io.IOException JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Iterator JavaDoc;
23
24 /** An alternative to BooleanScorer.
25  * <br>Uses ConjunctionScorer, DisjunctionScorer, ReqOptScorer and ReqExclScorer.
26  * <br>Implements skipTo(), and has no limitations on the numbers of added scorers.
27  */

28 class BooleanScorer2 extends Scorer {
29   private ArrayList JavaDoc requiredScorers = new ArrayList JavaDoc();
30   private ArrayList JavaDoc optionalScorers = new ArrayList JavaDoc();
31   private ArrayList JavaDoc prohibitedScorers = new ArrayList JavaDoc();
32
33
34   private class Coordinator {
35     int maxCoord = 0; // to be increased for each non prohibited scorer
36

37     private float[] coordFactors = null;
38     
39     void init() { // use after all scorers have been added.
40
coordFactors = new float[maxCoord + 1];
41       Similarity sim = getSimilarity();
42       for (int i = 0; i <= maxCoord; i++) {
43         coordFactors[i] = sim.coord(i, maxCoord);
44       }
45     }
46     
47     int nrMatchers; // to be increased by score() of match counting scorers.
48

49     void initDoc() {
50       nrMatchers = 0;
51     }
52     
53     float coordFactor() {
54       return coordFactors[nrMatchers];
55     }
56   }
57
58   private final Coordinator coordinator;
59
60   /** The scorer to which all scoring will be delegated,
61    * except for computing and using the coordination factor.
62    */

63   private Scorer countingSumScorer = null;
64
65   /** The number of optionalScorers that need to match (if there are any) */
66   private final int minNrShouldMatch;
67
68   /** Create a BooleanScorer2.
69    * @param similarity The similarity to be used.
70    * @param minNrShouldMatch The minimum number of optional added scorers
71    * that should match during the search.
72    * In case no required scorers are added,
73    * at least one of the optional scorers will have to
74    * match during the search.
75    */

76   public BooleanScorer2(Similarity similarity, int minNrShouldMatch) {
77     super(similarity);
78     if (minNrShouldMatch < 0) {
79       throw new IllegalArgumentException JavaDoc("Minimum number of optional scorers should not be negative");
80     }
81     coordinator = new Coordinator();
82     this.minNrShouldMatch = minNrShouldMatch;
83   }
84
85   /** Create a BooleanScorer2.
86    * In no required scorers are added,
87    * at least one of the optional scorers will have to match during the search.
88    * @param similarity The similarity to be used.
89    */

90   public BooleanScorer2(Similarity similarity) {
91     this(similarity, 0);
92   }
93
94   public void add(final Scorer scorer, boolean required, boolean prohibited) {
95     if (!prohibited) {
96       coordinator.maxCoord++;
97     }
98
99     if (required) {
100       if (prohibited) {
101         throw new IllegalArgumentException JavaDoc("scorer cannot be required and prohibited");
102       }
103       requiredScorers.add(scorer);
104     } else if (prohibited) {
105       prohibitedScorers.add(scorer);
106     } else {
107       optionalScorers.add(scorer);
108     }
109   }
110
111   /** Initialize the match counting scorer that sums all the
112    * scores. <p>
113    * When "counting" is used in a name it means counting the number
114    * of matching scorers.<br>
115    * When "sum" is used in a name it means score value summing
116    * over the matching scorers
117    */

118   private void initCountingSumScorer() {
119     coordinator.init();
120     countingSumScorer = makeCountingSumScorer();
121   }
122
123   /** Count a scorer as a single match. */
124   private class SingleMatchScorer extends Scorer {
125     private Scorer scorer;
126     private int lastScoredDoc = -1;
127
128     SingleMatchScorer(Scorer scorer) {
129       super(scorer.getSimilarity());
130       this.scorer = scorer;
131     }
132     public float score() throws IOException JavaDoc {
133       if (doc() > lastScoredDoc) {
134         lastScoredDoc = doc();
135         coordinator.nrMatchers++;
136       }
137       return scorer.score();
138     }
139     public int doc() {
140       return scorer.doc();
141     }
142     public boolean next() throws IOException JavaDoc {
143       return scorer.next();
144     }
145     public boolean skipTo(int docNr) throws IOException JavaDoc {
146       return scorer.skipTo(docNr);
147     }
148     public Explanation explain(int docNr) throws IOException JavaDoc {
149       return scorer.explain(docNr);
150     }
151   }
152
153   private Scorer countingDisjunctionSumScorer(List JavaDoc scorers,
154                                               int minMrShouldMatch)
155   // each scorer from the list counted as a single matcher
156
{
157     return new DisjunctionSumScorer(scorers, minMrShouldMatch) {
158       private int lastScoredDoc = -1;
159       public float score() throws IOException JavaDoc {
160         if (doc() > lastScoredDoc) {
161           lastScoredDoc = doc();
162           coordinator.nrMatchers += super.nrMatchers;
163         }
164         return super.score();
165       }
166     };
167   }
168
169   private static Similarity defaultSimilarity = new DefaultSimilarity();
170
171   private Scorer countingConjunctionSumScorer(List JavaDoc requiredScorers) {
172     // each scorer from the list counted as a single matcher
173
final int requiredNrMatchers = requiredScorers.size();
174     ConjunctionScorer cs = new ConjunctionScorer(defaultSimilarity) {
175       private int lastScoredDoc = -1;
176
177       public float score() throws IOException JavaDoc {
178         if (doc() > lastScoredDoc) {
179           lastScoredDoc = doc();
180           coordinator.nrMatchers += requiredNrMatchers;
181         }
182         // All scorers match, so defaultSimilarity super.score() always has 1 as
183
// the coordination factor.
184
// Therefore the sum of the scores of the requiredScorers
185
// is used as score.
186
return super.score();
187       }
188     };
189     Iterator JavaDoc rsi = requiredScorers.iterator();
190     while (rsi.hasNext()) {
191       cs.add((Scorer) rsi.next());
192     }
193     return cs;
194   }
195
196   private Scorer dualConjunctionSumScorer(Scorer req1, Scorer req2) { // non counting.
197
final int requiredNrMatchers = requiredScorers.size();
198     ConjunctionScorer cs = new ConjunctionScorer(defaultSimilarity);
199     // All scorers match, so defaultSimilarity super.score() always has 1 as
200
// the coordination factor.
201
// Therefore the sum of the scores of two scorers
202
// is used as score.
203
cs.add(req1);
204     cs.add(req2);
205     return cs;
206   }
207
208   /** Returns the scorer to be used for match counting and score summing.
209    * Uses requiredScorers, optionalScorers and prohibitedScorers.
210    */

211   private Scorer makeCountingSumScorer() { // each scorer counted as a single matcher
212
return (requiredScorers.size() == 0)
213           ? makeCountingSumScorerNoReq()
214           : makeCountingSumScorerSomeReq();
215   }
216
217   private Scorer makeCountingSumScorerNoReq() { // No required scorers
218
if (optionalScorers.size() == 0) {
219       return new NonMatchingScorer(); // no clauses or only prohibited clauses
220
} else { // No required scorers. At least one optional scorer.
221
// minNrShouldMatch optional scorers are required, but at least 1
222
int nrOptRequired = (minNrShouldMatch < 1) ? 1 : minNrShouldMatch;
223       if (optionalScorers.size() < nrOptRequired) {
224         return new NonMatchingScorer(); // fewer optional clauses than minimum (at least 1) that should match
225
} else { // optionalScorers.size() >= nrOptRequired, no required scorers
226
Scorer requiredCountingSumScorer =
227               (optionalScorers.size() > nrOptRequired)
228               ? countingDisjunctionSumScorer(optionalScorers, nrOptRequired)
229               : // optionalScorers.size() == nrOptRequired (all optional scorers are required), no required scorers
230
(optionalScorers.size() == 1)
231               ? new SingleMatchScorer((Scorer) optionalScorers.get(0))
232               : countingConjunctionSumScorer(optionalScorers);
233         return addProhibitedScorers( requiredCountingSumScorer);
234       }
235     }
236   }
237
238   private Scorer makeCountingSumScorerSomeReq() { // At least one required scorer.
239
if (optionalScorers.size() < minNrShouldMatch) {
240       return new NonMatchingScorer(); // fewer optional clauses than minimum that should match
241
} else if (optionalScorers.size() == minNrShouldMatch) { // all optional scorers also required.
242
ArrayList JavaDoc allReq = new ArrayList JavaDoc(requiredScorers);
243       allReq.addAll(optionalScorers);
244       return addProhibitedScorers( countingConjunctionSumScorer(allReq));
245     } else { // optionalScorers.size() > minNrShouldMatch, and at least one required scorer
246
Scorer requiredCountingSumScorer =
247             (requiredScorers.size() == 1)
248             ? new SingleMatchScorer((Scorer) requiredScorers.get(0))
249             : countingConjunctionSumScorer(requiredScorers);
250       if (minNrShouldMatch > 0) { // use a required disjunction scorer over the optional scorers
251
return addProhibitedScorers(
252                       dualConjunctionSumScorer( // non counting
253
requiredCountingSumScorer,
254                               countingDisjunctionSumScorer(
255                                       optionalScorers,
256                                       minNrShouldMatch)));
257       } else { // minNrShouldMatch == 0
258
return new ReqOptSumScorer(
259                       addProhibitedScorers(requiredCountingSumScorer),
260                       ((optionalScorers.size() == 1)
261                         ? new SingleMatchScorer((Scorer) optionalScorers.get(0))
262                         : countingDisjunctionSumScorer(optionalScorers, 1))); // require 1 in combined, optional scorer.
263
}
264     }
265   }
266   
267   /** Returns the scorer to be used for match counting and score summing.
268    * Uses the given required scorer and the prohibitedScorers.
269    * @param requiredCountingSumScorer A required scorer already built.
270    */

271   private Scorer addProhibitedScorers(Scorer requiredCountingSumScorer)
272   {
273     return (prohibitedScorers.size() == 0)
274           ? requiredCountingSumScorer // no prohibited
275
: new ReqExclScorer(requiredCountingSumScorer,
276                               ((prohibitedScorers.size() == 1)
277                                 ? (Scorer) prohibitedScorers.get(0)
278                                 : new DisjunctionSumScorer(prohibitedScorers)));
279   }
280
281   /** Scores and collects all matching documents.
282    * @param hc The collector to which all matching documents are passed through
283    * {@link HitCollector#collect(int, float)}.
284    * <br>When this method is used the {@link #explain(int)} method should not be used.
285    */

286   public void score(HitCollector hc) throws IOException JavaDoc {
287     if (countingSumScorer == null) {
288       initCountingSumScorer();
289     }
290     while (countingSumScorer.next()) {
291       hc.collect(countingSumScorer.doc(), score());
292     }
293   }
294
295   /** Expert: Collects matching documents in a range.
296    * <br>Note that {@link #next()} must be called once before this method is
297    * called for the first time.
298    * @param hc The collector to which all matching documents are passed through
299    * {@link HitCollector#collect(int, float)}.
300    * @param max Do not score documents past this.
301    * @return true if more matching documents may remain.
302    */

303   protected boolean score(HitCollector hc, int max) throws IOException JavaDoc {
304     // null pointer exception when next() was not called before:
305
int docNr = countingSumScorer.doc();
306     while (docNr < max) {
307       hc.collect(docNr, score());
308       if (! countingSumScorer.next()) {
309         return false;
310       }
311       docNr = countingSumScorer.doc();
312     }
313     return true;
314   }
315
316   public int doc() { return countingSumScorer.doc(); }
317
318   public boolean next() throws IOException JavaDoc {
319     if (countingSumScorer == null) {
320       initCountingSumScorer();
321     }
322     return countingSumScorer.next();
323   }
324
325   public float score() throws IOException JavaDoc {
326     coordinator.initDoc();
327     float sum = countingSumScorer.score();
328     return sum * coordinator.coordFactor();
329   }
330
331   /** Skips to the first match beyond the current whose document number is
332    * greater than or equal to a given target.
333    *
334    * <p>When this method is used the {@link #explain(int)} method should not be used.
335    *
336    * @param target The target document number.
337    * @return true iff there is such a match.
338    */

339   public boolean skipTo(int target) throws IOException JavaDoc {
340     if (countingSumScorer == null) {
341       initCountingSumScorer();
342     }
343     return countingSumScorer.skipTo(target);
344   }
345
346   /** Throws an UnsupportedOperationException.
347    * TODO: Implement an explanation of the coordination factor.
348    * @param doc The document number for the explanation.
349    * @throws UnsupportedOperationException
350    */

351   public Explanation explain(int doc) {
352     throw new UnsupportedOperationException JavaDoc();
353  /* How to explain the coordination factor?
354     initCountingSumScorer();
355     return countingSumScorer.explain(doc); // misses coord factor.
356   */

357   }
358 }
359
360
Popular Tags