add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / search / FilteredQuery.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 org.apache.lucene.index.IndexReader;
21 import org.apache.lucene.index.Term;
22 import org.apache.lucene.util.ToStringUtils;
23
24 import java.io.IOException;
25 import java.util.Set;
26
27
28 /**
29  * A query that applies a filter to the results of another query.
30  *
31  * <p>Note: the bits are retrieved from the filter each time this
32  * query is used in a search - use a CachingWrapperFilter to avoid
33  * regenerating the bits every time.
34  *
35  * <p>Created: Apr 20, 2004 8:58:29 AM
36  *
37  * @since   1.4
38  * @see     CachingWrapperFilter
39  */
40 public class FilteredQuery
41 extends Query {
42
43   Query query;
44   Filter filter;
45
46   /**
47    * Constructs a new query which applies a filter to the results of the original query.
48    * Filter.getDocIdSet() will be called every time this query is used in a search.
49    * @param query  Query to be filtered, cannot be <code>null</code>.
50    * @param filter Filter to apply to query results, cannot be <code>null</code>.
51    */
52   public FilteredQuery (Query query, Filter filter) {
53     this.query = query;
54     this.filter = filter;
55   }
56
57   /**
58    * Returns a Weight that applies the filter to the enclosed query's Weight.
59    * This is accomplished by overriding the Scorer returned by the Weight.
60    */
61   @Override
62   public Weight createWeight(final Searcher searcher) throws IOException {
63     final Weight weight = query.createWeight (searcher);
64     final Similarity similarity = query.getSimilarity(searcher);
65     return new Weight() {
66       private float value;
67         
68       // pass these methods through to enclosed query's weight
69       @Override
70       public float getValue() { return value; }
71       
72       @Override
73       public float sumOfSquaredWeights() throws IOException { 
74         return weight.sumOfSquaredWeights() * getBoost() * getBoost(); 
75       }
76
77       @Override
78       public void normalize (float v) { 
79         weight.normalize(v);
80         value = weight.getValue() * getBoost();
81       }
82
83       @Override
84       public Explanation explain (IndexReader ir, int i) throws IOException {
85         Explanation inner = weight.explain (ir, i);
86         if (getBoost()!=1) {
87           Explanation preBoost = inner;
88           inner = new Explanation(inner.getValue()*getBoost(),"product of:");
89           inner.addDetail(new Explanation(getBoost(),"boost"));
90           inner.addDetail(preBoost);
91         }
92         Filter f = FilteredQuery.this.filter;
93         DocIdSet docIdSet = f.getDocIdSet(ir);
94         DocIdSetIterator docIdSetIterator = docIdSet == null ? DocIdSet.EMPTY_DOCIDSET.iterator() : docIdSet.iterator();
95         if (docIdSetIterator == null) {
96           docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
97         }
98         if (docIdSetIterator.advance(i) == i) {
99           return inner;
100         } else {
101           Explanation result = new Explanation
102             (0.0f, "failure to match filter: " + f.toString());
103           result.addDetail(inner);
104           return result;
105         }
106       }
107
108       // return this query
109       @Override
110       public Query getQuery() { return FilteredQuery.this; }
111
112       // return a filtering scorer
113       @Override
114       public Scorer scorer(IndexReader indexReader, boolean scoreDocsInOrder, boolean topScorer)
115           throws IOException {
116         final Scorer scorer = weight.scorer(indexReader, true, false);
117         if (scorer == null) {
118           return null;
119         }
120         DocIdSet docIdSet = filter.getDocIdSet(indexReader);
121         if (docIdSet == null) {
122           return null;
123         }
124         final DocIdSetIterator docIdSetIterator = docIdSet.iterator();
125         if (docIdSetIterator == null) {
126           return null;
127         }
128
129         return new Scorer(similarity, this) {
130
131           private int doc = -1;
132           
133           private int advanceToCommon(int scorerDoc, int disiDoc) throws IOException {
134             while (scorerDoc != disiDoc) {
135               if (scorerDoc < disiDoc) {
136                 scorerDoc = scorer.advance(disiDoc);
137               } else {
138                 disiDoc = docIdSetIterator.advance(scorerDoc);
139               }
140             }
141             return scorerDoc;
142           }
143
144           @Override
145           public int nextDoc() throws IOException {
146             int scorerDoc, disiDoc;
147             return doc = (disiDoc = docIdSetIterator.nextDoc()) != NO_MORE_DOCS
148                 && (scorerDoc = scorer.nextDoc()) != NO_MORE_DOCS
149                 && advanceToCommon(scorerDoc, disiDoc) != NO_MORE_DOCS ? scorer.docID() : NO_MORE_DOCS;
150           }
151           
152           @Override
153           public int docID() { return doc; }
154           
155           @Override
156           public int advance(int target) throws IOException {
157             int disiDoc, scorerDoc;
158             return doc = (disiDoc = docIdSetIterator.advance(target)) != NO_MORE_DOCS
159                 && (scorerDoc = scorer.advance(disiDoc)) != NO_MORE_DOCS 
160                 && advanceToCommon(scorerDoc, disiDoc) != NO_MORE_DOCS ? scorer.docID() : NO_MORE_DOCS;
161           }
162
163           @Override
164           public float score() throws IOException { return getBoost() * scorer.score(); }
165         };
166       }
167     };
168   }
169
170   /** Rewrites the wrapped query. */
171   @Override
172   public Query rewrite(IndexReader reader) throws IOException {
173     Query rewritten = query.rewrite(reader);
174     if (rewritten != query) {
175       FilteredQuery clone = (FilteredQuery)this.clone();
176       clone.query = rewritten;
177       return clone;
178     } else {
179       return this;
180     }
181   }
182
183   public Query getQuery() {
184     return query;
185   }
186
187   public Filter getFilter() {
188     return filter;
189   }
190
191   // inherit javadoc
192   @Override
193   public void extractTerms(Set<Term> terms) {
194       getQuery().extractTerms(terms);
195   }
196
197   /** Prints a user-readable version of this query. */
198   @Override
199   public String toString (String s) {
200     StringBuilder buffer = new StringBuilder();
201     buffer.append("filtered(");
202     buffer.append(query.toString(s));
203     buffer.append(")->");
204     buffer.append(filter);
205     buffer.append(ToStringUtils.boost(getBoost()));
206     return buffer.toString();
207   }
208
209   /** Returns true iff <code>o</code> is equal to this. */
210   @Override
211   public boolean equals(Object o) {
212     if (o instanceof FilteredQuery) {
213       FilteredQuery fq = (FilteredQuery) o;
214       return (query.equals(fq.query) && filter.equals(fq.filter) && getBoost()==fq.getBoost());
215     }
216     return false;
217   }
218
219   /** Returns a hash code value for this object. */
220   @Override
221   public int hashCode() {
222     return query.hashCode() ^ filter.hashCode() + Float.floatToRawIntBits(getBoost());
223   }
224 }