1 package org.apache.lucene.search;
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 import org.apache.lucene.index.IndexReader;
21 import org.apache.lucene.index.Term;
22 import org.apache.lucene.util.ToStringUtils;
24 import java.io.IOException;
29 * A query that applies a filter to the results of another query.
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.
35 * <p>Created: Apr 20, 2004 8:58:29 AM
38 * @see CachingWrapperFilter
40 public class FilteredQuery
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>.
52 public FilteredQuery (Query query, Filter filter) {
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.
62 public Weight createWeight(final Searcher searcher) throws IOException {
63 final Weight weight = query.createWeight (searcher);
64 final Similarity similarity = query.getSimilarity(searcher);
68 // pass these methods through to enclosed query's weight
70 public float getValue() { return value; }
73 public float sumOfSquaredWeights() throws IOException {
74 return weight.sumOfSquaredWeights() * getBoost() * getBoost();
78 public void normalize (float v) {
80 value = weight.getValue() * getBoost();
84 public Explanation explain (IndexReader ir, int i) throws IOException {
85 Explanation inner = weight.explain (ir, i);
87 Explanation preBoost = inner;
88 inner = new Explanation(inner.getValue()*getBoost(),"product of:");
89 inner.addDetail(new Explanation(getBoost(),"boost"));
90 inner.addDetail(preBoost);
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();
98 if (docIdSetIterator.advance(i) == i) {
101 Explanation result = new Explanation
102 (0.0f, "failure to match filter: " + f.toString());
103 result.addDetail(inner);
110 public Query getQuery() { return FilteredQuery.this; }
112 // return a filtering scorer
114 public Scorer scorer(IndexReader indexReader, boolean scoreDocsInOrder, boolean topScorer)
116 final Scorer scorer = weight.scorer(indexReader, true, false);
117 if (scorer == null) {
120 DocIdSet docIdSet = filter.getDocIdSet(indexReader);
121 if (docIdSet == null) {
124 final DocIdSetIterator docIdSetIterator = docIdSet.iterator();
125 if (docIdSetIterator == null) {
129 return new Scorer(similarity, this) {
131 private int doc = -1;
133 private int advanceToCommon(int scorerDoc, int disiDoc) throws IOException {
134 while (scorerDoc != disiDoc) {
135 if (scorerDoc < disiDoc) {
136 scorerDoc = scorer.advance(disiDoc);
138 disiDoc = docIdSetIterator.advance(scorerDoc);
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;
153 public int docID() { return doc; }
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;
164 public float score() throws IOException { return getBoost() * scorer.score(); }
170 /** Rewrites the wrapped query. */
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;
183 public Query getQuery() {
187 public Filter getFilter() {
193 public void extractTerms(Set<Term> terms) {
194 getQuery().extractTerms(terms);
197 /** Prints a user-readable version of this query. */
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();
209 /** Returns true iff <code>o</code> is equal to this. */
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());
219 /** Returns a hash code value for this object. */
221 public int hashCode() {
222 return query.hashCode() ^ filter.hashCode() + Float.floatToRawIntBits(getBoost());