add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / search / MultiTermQuery.java
1 package org.apache.lucene.search;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.IOException;
21 import java.io.Serializable;
22
23 import org.apache.lucene.index.IndexReader;
24 import org.apache.lucene.index.Term;
25
26 import org.apache.lucene.queryParser.QueryParser; // for javadoc
27
28 /**
29  * An abstract {@link Query} that matches documents
30  * containing a subset of terms provided by a {@link
31  * FilteredTermEnum} enumeration.
32  *
33  * <p>This query cannot be used directly; you must subclass
34  * it and define {@link #getEnum} to provide a {@link
35  * FilteredTermEnum} that iterates through the terms to be
36  * matched.
37  *
38  * <p><b>NOTE</b>: if {@link #setRewriteMethod} is either
39  * {@link #CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE} or {@link
40  * #SCORING_BOOLEAN_QUERY_REWRITE}, you may encounter a
41  * {@link BooleanQuery.TooManyClauses} exception during
42  * searching, which happens when the number of terms to be
43  * searched exceeds {@link
44  * BooleanQuery#getMaxClauseCount()}.  Setting {@link
45  * #setRewriteMethod} to {@link #CONSTANT_SCORE_FILTER_REWRITE}
46  * prevents this.
47  *
48  * <p>The recommended rewrite method is {@link
49  * #CONSTANT_SCORE_AUTO_REWRITE_DEFAULT}: it doesn't spend CPU
50  * computing unhelpful scores, and it tries to pick the most
51  * performant rewrite method given the query. If you
52  * need scoring (like {@link FuzzyQuery}, use
53  * {@link TopTermsScoringBooleanQueryRewrite} which uses
54  * a priority queue to only collect competitive terms
55  * and not hit this limitation.
56  *
57  * Note that {@link QueryParser} produces
58  * MultiTermQueries using {@link
59  * #CONSTANT_SCORE_AUTO_REWRITE_DEFAULT} by default.
60  */
61 public abstract class MultiTermQuery extends Query {
62   protected RewriteMethod rewriteMethod = CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
63   transient int numberOfTerms = 0;
64
65   /** Abstract class that defines how the query is rewritten. */
66   public static abstract class RewriteMethod implements Serializable {
67     public abstract Query rewrite(IndexReader reader, MultiTermQuery query) throws IOException;
68   }
69
70   /** A rewrite method that first creates a private Filter,
71    *  by visiting each term in sequence and marking all docs
72    *  for that term.  Matching documents are assigned a
73    *  constant score equal to the query's boost.
74    * 
75    *  <p> This method is faster than the BooleanQuery
76    *  rewrite methods when the number of matched terms or
77    *  matched documents is non-trivial. Also, it will never
78    *  hit an errant {@link BooleanQuery.TooManyClauses}
79    *  exception.
80    *
81    *  @see #setRewriteMethod */
82   public static final RewriteMethod CONSTANT_SCORE_FILTER_REWRITE = new RewriteMethod() {
83     @Override
84     public Query rewrite(IndexReader reader, MultiTermQuery query) {
85       Query result = new ConstantScoreQuery(new MultiTermQueryWrapperFilter<MultiTermQuery>(query));
86       result.setBoost(query.getBoost());
87       return result;
88     }
89
90     // Make sure we are still a singleton even after deserializing
91     protected Object readResolve() {
92       return CONSTANT_SCORE_FILTER_REWRITE;
93     }
94   };
95
96   /** A rewrite method that first translates each term into
97    *  {@link BooleanClause.Occur#SHOULD} clause in a
98    *  BooleanQuery, and keeps the scores as computed by the
99    *  query.  Note that typically such scores are
100    *  meaningless to the user, and require non-trivial CPU
101    *  to compute, so it's almost always better to use {@link
102    *  #CONSTANT_SCORE_AUTO_REWRITE_DEFAULT} instead.
103    *
104    *  <p><b>NOTE</b>: This rewrite method will hit {@link
105    *  BooleanQuery.TooManyClauses} if the number of terms
106    *  exceeds {@link BooleanQuery#getMaxClauseCount}.
107    *
108    *  @see #setRewriteMethod */
109   public final static RewriteMethod SCORING_BOOLEAN_QUERY_REWRITE = ScoringRewrite.SCORING_BOOLEAN_QUERY_REWRITE;
110   
111   /** Like {@link #SCORING_BOOLEAN_QUERY_REWRITE} except
112    *  scores are not computed.  Instead, each matching
113    *  document receives a constant score equal to the
114    *  query's boost.
115    * 
116    *  <p><b>NOTE</b>: This rewrite method will hit {@link
117    *  BooleanQuery.TooManyClauses} if the number of terms
118    *  exceeds {@link BooleanQuery#getMaxClauseCount}.
119    *
120    *  @see #setRewriteMethod */
121   public final static RewriteMethod CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE = ScoringRewrite.CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE;
122
123   /**
124    * A rewrite method that first translates each term into
125    * {@link BooleanClause.Occur#SHOULD} clause in a BooleanQuery, and keeps the
126    * scores as computed by the query.
127    * 
128    * <p>
129    * This rewrite method only uses the top scoring terms so it will not overflow
130    * the boolean max clause count. It is the default rewrite method for
131    * {@link FuzzyQuery}.
132    * 
133    * @see #setRewriteMethod
134    */
135   public static final class TopTermsScoringBooleanQueryRewrite extends TopTermsRewrite<BooleanQuery> {
136
137     /** 
138      * Create a TopTermsScoringBooleanQueryRewrite for 
139      * at most <code>size</code> terms.
140      * <p>
141      * NOTE: if {@link BooleanQuery#getMaxClauseCount} is smaller than 
142      * <code>size</code>, then it will be used instead. 
143      */
144     public TopTermsScoringBooleanQueryRewrite(int size) {
145       super(size);
146     }
147     
148     @Override
149     protected int getMaxSize() {
150       return BooleanQuery.getMaxClauseCount();
151     }
152     
153     @Override
154     protected BooleanQuery getTopLevelQuery() {
155       return new BooleanQuery(true);
156     }
157     
158     @Override
159     protected void addClause(BooleanQuery topLevel, Term term, float boost) {
160       final TermQuery tq = new TermQuery(term);
161       tq.setBoost(boost);
162       topLevel.add(tq, BooleanClause.Occur.SHOULD);
163     }
164   }
165   
166   /**
167    * A rewrite method that first translates each term into
168    * {@link BooleanClause.Occur#SHOULD} clause in a BooleanQuery, but the scores
169    * are only computed as the boost.
170    * <p>
171    * This rewrite method only uses the top scoring terms so it will not overflow
172    * the boolean max clause count.
173    * 
174    * @see #setRewriteMethod
175    */
176   public static final class TopTermsBoostOnlyBooleanQueryRewrite extends TopTermsRewrite<BooleanQuery> {
177     
178     /** 
179      * Create a TopTermsBoostOnlyBooleanQueryRewrite for 
180      * at most <code>size</code> terms.
181      * <p>
182      * NOTE: if {@link BooleanQuery#getMaxClauseCount} is smaller than 
183      * <code>size</code>, then it will be used instead. 
184      */
185     public TopTermsBoostOnlyBooleanQueryRewrite(int size) {
186       super(size);
187     }
188     
189     @Override
190     protected int getMaxSize() {
191       return BooleanQuery.getMaxClauseCount();
192     }
193     
194     @Override
195     protected BooleanQuery getTopLevelQuery() {
196       return new BooleanQuery(true);
197     }
198     
199     @Override
200     protected void addClause(BooleanQuery topLevel, Term term, float boost) {
201       final Query q = new ConstantScoreQuery(new TermQuery(term));
202       q.setBoost(boost);
203       topLevel.add(q, BooleanClause.Occur.SHOULD);
204     }
205   }
206     
207   /** A rewrite method that tries to pick the best
208    *  constant-score rewrite method based on term and
209    *  document counts from the query.  If both the number of
210    *  terms and documents is small enough, then {@link
211    *  #CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE} is used.
212    *  Otherwise, {@link #CONSTANT_SCORE_FILTER_REWRITE} is
213    *  used.
214    */
215   public static class ConstantScoreAutoRewrite extends org.apache.lucene.search.ConstantScoreAutoRewrite {}
216
217   /** Read-only default instance of {@link
218    *  ConstantScoreAutoRewrite}, with {@link
219    *  ConstantScoreAutoRewrite#setTermCountCutoff} set to
220    *  {@link
221    *  ConstantScoreAutoRewrite#DEFAULT_TERM_COUNT_CUTOFF}
222    *  and {@link
223    *  ConstantScoreAutoRewrite#setDocCountPercent} set to
224    *  {@link
225    *  ConstantScoreAutoRewrite#DEFAULT_DOC_COUNT_PERCENT}.
226    *  Note that you cannot alter the configuration of this
227    *  instance; you'll need to create a private instance
228    *  instead. */
229   public final static RewriteMethod CONSTANT_SCORE_AUTO_REWRITE_DEFAULT = new ConstantScoreAutoRewrite() {
230     @Override
231     public void setTermCountCutoff(int count) {
232       throw new UnsupportedOperationException("Please create a private instance");
233     }
234
235     @Override
236     public void setDocCountPercent(double percent) {
237       throw new UnsupportedOperationException("Please create a private instance");
238     }
239
240     // Make sure we are still a singleton even after deserializing
241     protected Object readResolve() {
242       return CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
243     }
244   };
245
246   /**
247    * Constructs a query matching terms that cannot be represented with a single
248    * Term.
249    */
250   public MultiTermQuery() {
251   }
252
253   /** Construct the enumeration to be used, expanding the pattern term. */
254   protected abstract FilteredTermEnum getEnum(IndexReader reader)
255       throws IOException;
256
257   /**
258    * Expert: Return the number of unique terms visited during execution of the query.
259    * If there are many of them, you may consider using another query type
260    * or optimize your total term count in index.
261    * <p>This method is not thread safe, be sure to only call it when no query is running!
262    * If you re-use the same query instance for another
263    * search, be sure to first reset the term counter
264    * with {@link #clearTotalNumberOfTerms}.
265    * <p>On optimized indexes / no MultiReaders, you get the correct number of
266    * unique terms for the whole index. Use this number to compare different queries.
267    * For non-optimized indexes this number can also be achieved in
268    * non-constant-score mode. In constant-score mode you get the total number of
269    * terms seeked for all segments / sub-readers.
270    * @see #clearTotalNumberOfTerms
271    */
272   public int getTotalNumberOfTerms() {
273     return numberOfTerms;
274   }
275   
276   /**
277    * Expert: Resets the counting of unique terms.
278    * Do this before executing the query/filter.
279    * @see #getTotalNumberOfTerms
280    */
281   public void clearTotalNumberOfTerms() {
282     numberOfTerms = 0;
283   }
284   
285   protected void incTotalNumberOfTerms(int inc) {
286     numberOfTerms += inc;
287   }
288
289   /**
290    * To rewrite to a simpler form, instead return a simpler
291    * enum from {@link #getEnum(IndexReader)}.  For example,
292    * to rewrite to a single term, return a {@link SingleTermEnum}
293    */
294   @Override
295   public final Query rewrite(IndexReader reader) throws IOException {
296     return rewriteMethod.rewrite(reader, this);
297   }
298
299   /**
300    * @see #setRewriteMethod
301    */
302   public RewriteMethod getRewriteMethod() {
303     return rewriteMethod;
304   }
305
306   /**
307    * Sets the rewrite method to be used when executing the
308    * query.  You can use one of the four core methods, or
309    * implement your own subclass of {@link RewriteMethod}. */
310   public void setRewriteMethod(RewriteMethod method) {
311     rewriteMethod = method;
312   }
313
314   @Override
315   public int hashCode() {
316     final int prime = 31;
317     int result = 1;
318     result = prime * result + Float.floatToIntBits(getBoost());
319     result = prime * result;
320     result += rewriteMethod.hashCode();
321     return result;
322   }
323
324   @Override
325   public boolean equals(Object obj) {
326     if (this == obj)
327       return true;
328     if (obj == null)
329       return false;
330     if (getClass() != obj.getClass())
331       return false;
332     MultiTermQuery other = (MultiTermQuery) obj;
333     if (Float.floatToIntBits(getBoost()) != Float.floatToIntBits(other.getBoost()))
334       return false;
335     if (!rewriteMethod.equals(other.rewriteMethod)) {
336       return false;
337     }
338     return true;
339   }
340  
341 }