KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.List JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.io.IOException JavaDoc;
22
23 import org.apache.lucene.util.PriorityQueue;
24
25 /** A Scorer for OR like queries, counterpart of Lucene's <code>ConjunctionScorer</code>.
26  * This Scorer implements {@link Scorer#skipTo(int)} and uses skipTo() on the given Scorers.
27  */

28 public class DisjunctionSumScorer extends Scorer {
29   /** The number of subscorers. */
30   private final int nrScorers;
31   
32   /** The subscorers. */
33   protected final List JavaDoc subScorers;
34   
35   /** The minimum number of scorers that should match. */
36   private final int minimumNrMatchers;
37   
38   /** The scorerQueue contains all subscorers ordered by their current doc(),
39    * with the minimum at the top.
40    * <br>The scorerQueue is initialized the first time next() or skipTo() is called.
41    * <br>An exhausted scorer is immediately removed from the scorerQueue.
42    * <br>If less than the minimumNrMatchers scorers
43    * remain in the scorerQueue next() and skipTo() return false.
44    * <p>
45    * After each to call to next() or skipTo()
46    * <code>currentSumScore</code> is the total score of the current matching doc,
47    * <code>nrMatchers</code> is the number of matching scorers,
48    * and all scorers are after the matching doc, or are exhausted.
49    */

50   private ScorerQueue scorerQueue = null;
51   
52   /** The document number of the current match. */
53   private int currentDoc = -1;
54
55   /** The number of subscorers that provide the current match. */
56   protected int nrMatchers = -1;
57
58   private float currentScore = Float.NaN;
59   
60   /** Construct a <code>DisjunctionScorer</code>.
61    * @param subScorers A collection of at least two subscorers.
62    * @param 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>.
69    */

70   public DisjunctionSumScorer( List JavaDoc subScorers, int minimumNrMatchers) {
71     super(null);
72     
73     nrScorers = subScorers.size();
74
75     if (minimumNrMatchers <= 0) {
76       throw new IllegalArgumentException JavaDoc("Minimum nr of matchers must be positive");
77     }
78     if (nrScorers <= 1) {
79       throw new IllegalArgumentException JavaDoc("There must be at least 2 subScorers");
80     }
81
82     this.minimumNrMatchers = minimumNrMatchers;
83     this.subScorers = subScorers;
84   }
85   
86   /** Construct a <code>DisjunctionScorer</code>, using one as the minimum number
87    * of matching subscorers.
88    */

89   public DisjunctionSumScorer(List JavaDoc subScorers) {
90     this(subScorers, 1);
91   }
92
93   /** Called the first time next() or skipTo() is called to
94    * initialize <code>scorerQueue</code>.
95    */

96   private void initScorerQueue() throws IOException JavaDoc {
97     Iterator JavaDoc si = subScorers.iterator();
98     scorerQueue = new ScorerQueue(nrScorers);
99     while (si.hasNext()) {
100       Scorer se = (Scorer) si.next();
101       if (se.next()) { // doc() method will be used in scorerQueue.
102
scorerQueue.insert(se);
103       }
104     }
105   }
106
107   /** A <code>PriorityQueue</code> that orders by {@link Scorer#doc()}. */
108   private class ScorerQueue extends PriorityQueue {
109     ScorerQueue(int size) {
110       initialize(size);
111     }
112
113     protected boolean lessThan(Object JavaDoc o1, Object JavaDoc o2) {
114       return ((Scorer)o1).doc() < ((Scorer)o2).doc();
115     }
116   }
117   
118   public boolean next() throws IOException JavaDoc {
119     if (scorerQueue == null) {
120       initScorerQueue();
121     }
122     if (scorerQueue.size() < minimumNrMatchers) {
123       return false;
124     } else {
125       return advanceAfterCurrent();
126     }
127   }
128
129
130   /** Advance all subscorers after the current document determined by the
131    * top of the <code>scorerQueue</code>.
132    * Repeat until at least the minimum number of subscorers match on the same
133    * document and all subscorers are after that document or are exhausted.
134    * <br>On entry the <code>scorerQueue</code> has at least <code>minimumNrMatchers</code>
135    * available. At least the scorer with the minimum document number will be advanced.
136    * @return true iff there is a match.
137    * <br>In case there is a match, </code>currentDoc</code>, </code>currentSumScore</code>,
138    * and </code>nrMatchers</code> describe the match.
139    *
140    * @todo Investigate whether it is possible to use skipTo() when
141    * the minimum number of matchers is bigger than one, ie. try and use the
142    * character of ConjunctionScorer for the minimum number of matchers.
143    */

144   protected boolean advanceAfterCurrent() throws IOException JavaDoc {
145     do { // repeat until minimum nr of matchers
146
Scorer top = (Scorer) scorerQueue.top();
147       currentDoc = top.doc();
148       currentScore = top.score();
149       nrMatchers = 1;
150       do { // Until all subscorers are after currentDoc
151
if (top.next()) {
152           scorerQueue.adjustTop();
153         } else {
154           scorerQueue.pop();
155           if (scorerQueue.size() < (minimumNrMatchers - nrMatchers)) {
156             // Not enough subscorers left for a match on this document,
157
// and also no more chance of any further match.
158
return false;
159           }
160           if (scorerQueue.size() == 0) {
161             break; // nothing more to advance, check for last match.
162
}
163         }
164         top = (Scorer) scorerQueue.top();
165         if (top.doc() != currentDoc) {
166           break; // All remaining subscorers are after currentDoc.
167
} else {
168           currentScore += top.score();
169           nrMatchers++;
170         }
171       } while (true);
172       
173       if (nrMatchers >= minimumNrMatchers) {
174         return true;
175       } else if (scorerQueue.size() < minimumNrMatchers) {
176         return false;
177       }
178     } while (true);
179   }
180   
181   /** Returns the score of the current document matching the query.
182    * Initially invalid, until {@link #next()} is called the first time.
183    */

184   public float score() throws IOException JavaDoc { return currentScore; }
185    
186   public int doc() { return currentDoc; }
187
188   /** Returns the number of subscorers matching the current document.
189    * Initially invalid, until {@link #next()} is called the first time.
190    */

191   public int nrMatchers() {
192     return nrMatchers;
193   }
194
195   /** Skips to the first match beyond the current whose document number is
196    * greater than or equal to a given target.
197    * <br>When this method is used the {@link #explain(int)} method should not be used.
198    * <br>The implementation uses the skipTo() method on the subscorers.
199    * @param target The target document number.
200    * @return true iff there is such a match.
201    */

202   public boolean skipTo(int target) throws IOException JavaDoc {
203     if (scorerQueue == null) {
204       initScorerQueue();
205     }
206     if (scorerQueue.size() < minimumNrMatchers) {
207       return false;
208     }
209     if (target <= currentDoc) {
210       target = currentDoc + 1;
211     }
212     do {
213       Scorer top = (Scorer) scorerQueue.top();
214       if (top.doc() >= target) {
215         return advanceAfterCurrent();
216       } else if (top.skipTo(target)) {
217         scorerQueue.adjustTop();
218       } else {
219         scorerQueue.pop();
220         if (scorerQueue.size() < minimumNrMatchers) {
221           return false;
222         }
223       }
224     } while (true);
225   }
226
227  /** Gives and explanation for the score of a given document.
228   * @todo Show the resulting score. See BooleanScorer.explain() on how to do this.
229   */

230   public Explanation explain(int doc) throws IOException JavaDoc {
231     Explanation res = new Explanation();
232     res.setDescription("At least " + minimumNrMatchers + " of");
233     Iterator JavaDoc ssi = subScorers.iterator();
234     while (ssi.hasNext()) {
235       res.addDetail( ((Scorer) ssi.next()).explain(doc));
236     }
237     return res;
238   }
239 }
240
Popular Tags