LUCENE-1518: Make Filter extend Query.

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1659585 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Adrien Grand 2015-02-13 15:59:03 +00:00
parent 0f7b913b07
commit 82eff4eb4d
16 changed files with 203 additions and 119 deletions

View File

@ -104,6 +104,9 @@ API Changes
* LUCENE-6223: Move BooleanQuery.BooleanWeight to BooleanWeight.
(Robert Muir)
* LUCENE-1518: Make Filter extend Query and return 0 as score.
(Uwe Schindler, Adrien Grand)
Other
* LUCENE-6193: Collapse identical catch branches in try-catch statements.

View File

@ -126,8 +126,8 @@ public class CachingWrapperFilter extends Filter implements Accountable {
}
@Override
public String toString() {
return getClass().getSimpleName() + "("+filter+")";
public String toString(String field) {
return getClass().getSimpleName() + "("+filter.toString(field)+")";
}
@Override

View File

@ -30,78 +30,47 @@ import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.ToStringUtils;
/**
* A query that wraps another query or a filter and simply returns a constant score equal to the
* query boost for every document that matches the filter or query.
* For queries it therefore simply strips of all scores and returns a constant one.
* A query that wraps another query and simply returns a constant score equal to the
* query boost for every document that matches the query.
* It therefore simply strips of all scores and returns a constant one.
*/
public class ConstantScoreQuery extends Query {
protected final Filter filter;
protected final Query query;
/** Strips off scores from the passed in Query. The hits will get a constant score
* dependent on the boost factor of this query. */
public ConstantScoreQuery(Query query) {
if (query == null)
throw new NullPointerException("Query may not be null");
this.filter = null;
this.query = query;
}
/** Wraps a Filter as a Query. The hits will get a constant score
* dependent on the boost factor of this query.
* If you simply want to strip off scores from a Query, no longer use
* {@code new ConstantScoreQuery(new QueryWrapperFilter(query))}, instead
* use {@link #ConstantScoreQuery(Query)}!
*/
public ConstantScoreQuery(Filter filter) {
if (filter == null)
throw new NullPointerException("Filter may not be null");
this.filter = filter;
this.query = null;
}
/** Returns the encapsulated filter, returns {@code null} if a query is wrapped. */
public Filter getFilter() {
return filter;
}
/** Returns the encapsulated query, returns {@code null} if a filter is wrapped. */
/** Returns the encapsulated query. */
public Query getQuery() {
return query;
}
@Override
public Query rewrite(IndexReader reader) throws IOException {
if (query != null) {
Query rewritten = query.rewrite(reader);
if (rewritten != query) {
rewritten = new ConstantScoreQuery(rewritten);
rewritten.setBoost(this.getBoost());
return rewritten;
}
} else {
assert filter != null;
// Fix outdated usage pattern from Lucene 2.x/early-3.x:
// because ConstantScoreQuery only accepted filters,
// QueryWrapperFilter was used to wrap queries.
if (filter instanceof QueryWrapperFilter) {
final QueryWrapperFilter qwf = (QueryWrapperFilter) filter;
final Query rewritten = new ConstantScoreQuery(qwf.getQuery().rewrite(reader));
rewritten.setBoost(this.getBoost());
return rewritten;
}
Query sub = query;
if (sub instanceof QueryWrapperFilter) {
sub = ((QueryWrapperFilter) sub).getQuery();
}
Query rewritten = sub.rewrite(reader);
if (rewritten != query) {
rewritten = new ConstantScoreQuery(rewritten);
rewritten.setBoost(this.getBoost());
return rewritten;
}
return this;
}
@Override
public void extractTerms(Set<Term> terms) {
// TODO: OK to not add any terms when wrapped a filter
// and used with MultiSearcher, but may not be OK for
// highlighting.
// If a query was wrapped, we delegate to query.
if (query != null)
// NOTE: ConstantScoreQuery used to wrap either a query or a filter. Now
// that filter extends Query, we need to only extract terms when the query
// is not a filter if we do not want to hit an UnsupportedOperationException
if (query instanceof Filter == false) {
query.extractTerms(terms);
}
}
protected class ConstantWeight extends Weight {
@ -111,13 +80,13 @@ public class ConstantScoreQuery extends Query {
public ConstantWeight(IndexSearcher searcher) throws IOException {
super(ConstantScoreQuery.this);
this.innerWeight = (query == null) ? null : query.createWeight(searcher, false);
this.innerWeight = query.createWeight(searcher, false);
}
@Override
public float getValueForNormalization() throws IOException {
// we calculate sumOfSquaredWeights of the inner weight, but ignore it (just to initialize everything)
if (innerWeight != null) innerWeight.getValueForNormalization();
innerWeight.getValueForNormalization();
queryWeight = getBoost();
return queryWeight * queryWeight;
}
@ -127,46 +96,25 @@ public class ConstantScoreQuery extends Query {
this.queryNorm = norm * topLevelBoost;
queryWeight *= this.queryNorm;
// we normalize the inner weight, but ignore it (just to initialize everything)
if (innerWeight != null) innerWeight.normalize(norm, topLevelBoost);
innerWeight.normalize(norm, topLevelBoost);
}
@Override
public BulkScorer bulkScorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
if (filter != null) {
assert query == null;
return super.bulkScorer(context, acceptDocs);
} else {
assert query != null && innerWeight != null;
BulkScorer bulkScorer = innerWeight.bulkScorer(context, acceptDocs);
if (bulkScorer == null) {
return null;
}
return new ConstantBulkScorer(bulkScorer, this, queryWeight);
BulkScorer bulkScorer = innerWeight.bulkScorer(context, acceptDocs);
if (bulkScorer == null) {
return null;
}
return new ConstantBulkScorer(bulkScorer, this, queryWeight);
}
@Override
public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
if (filter != null) {
assert query == null;
final DocIdSet dis = filter.getDocIdSet(context, acceptDocs);
if (dis == null) {
return null;
}
final DocIdSetIterator disi = dis.iterator();
if (disi == null)
return null;
return new ConstantDocIdSetIteratorScorer(disi, this, queryWeight);
} else {
assert query != null && innerWeight != null;
Scorer scorer = innerWeight.scorer(context, acceptDocs);
if (scorer == null) {
return null;
}
return new ConstantScoreScorer(scorer, queryWeight);
Scorer scorer = innerWeight.scorer(context, acceptDocs);
if (scorer == null) {
return null;
}
return new ConstantScoreScorer(scorer, queryWeight);
}
@Override
@ -247,11 +195,7 @@ public class ConstantScoreQuery extends Query {
@Override
public Collection<ChildScorer> getChildren() {
if (query != null) {
return Collections.singletonList(new ChildScorer(in, "constant"));
} else {
return Collections.emptyList();
}
return Collections.singletonList(new ChildScorer(in, "constant"));
}
}
@ -334,7 +278,7 @@ public class ConstantScoreQuery extends Query {
@Override
public String toString(String field) {
return new StringBuilder("ConstantScore(")
.append((query == null) ? filter.toString() : query.toString(field))
.append(query.toString(field))
.append(')')
.append(ToStringUtils.boost(getBoost()))
.toString();
@ -347,17 +291,14 @@ public class ConstantScoreQuery extends Query {
return false;
if (o instanceof ConstantScoreQuery) {
final ConstantScoreQuery other = (ConstantScoreQuery) o;
return
((this.filter == null) ? other.filter == null : this.filter.equals(other.filter)) &&
((this.query == null) ? other.query == null : this.query.equals(other.query));
return this.query.equals(other.query);
}
return false;
}
@Override
public int hashCode() {
return 31 * super.hashCode() +
((query == null) ? filter : query).hashCode();
return 31 * super.hashCode() + query.hashCode();
}
}

View File

@ -116,7 +116,7 @@ public abstract class DocTermOrdsRangeFilter extends Filter {
}
@Override
public final String toString() {
public final String toString(String defaultField) {
final StringBuilder sb = new StringBuilder(field).append(":");
return sb.append(includeLower ? '[' : '{')
.append((lowerVal == null) ? "*" : lowerVal.toString())

View File

@ -373,7 +373,7 @@ public abstract class DocValuesRangeFilter<T> extends Filter {
}
@Override
public final String toString() {
public final String toString(String defaultField) {
final StringBuilder sb = new StringBuilder(field).append(":");
return sb.append(includeLower ? '[' : '{')
.append((lowerVal == null) ? "*" : lowerVal.toString())

View File

@ -119,4 +119,17 @@ public class DocValuesTermsFilter extends Filter {
}
};
}
@Override
public String toString(String defaultField) {
StringBuilder sb = new StringBuilder();
sb.append(field).append(": [");
for (BytesRef term : terms) {
sb.append(term).append(", ");
}
if (terms.length > 0) {
sb.setLength(sb.length() - 2);
}
return sb.append(']').toString();
}
}

View File

@ -138,7 +138,7 @@ public class FieldValueFilter extends Filter {
}
@Override
public String toString() {
public String toString(String defaultField) {
return "FieldValueFilter [field=" + field + ", negate=" + negate + "]";
}

View File

@ -21,13 +21,14 @@ import java.io.IOException;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
/**
* Abstract base class for restricting which documents may
* be returned during searching.
/**
* Convenient base class for building queries that only perform matching, but
* no scoring. The scorer produced by such queries always returns 0 as score.
*/
public abstract class Filter {
public abstract class Filter extends Query {
/**
* Creates a {@link DocIdSet} enumerating the documents that should be
* permitted in search results. <b>NOTE:</b> null can be
@ -37,23 +38,139 @@ public abstract class Filter {
* the index during searching. The returned {@link DocIdSet}
* must refer to document IDs for that segment, not for
* the top-level reader.
*
*
* @param context a {@link org.apache.lucene.index.LeafReaderContext} instance opened on the index currently
* searched on. Note, it is likely that the provided reader info does not
* represent the whole underlying index i.e. if the index has more than
* one segment the given reader only represents a single segment.
* The provided context is always an atomic context, so you can call
* The provided context is always an atomic context, so you can call
* {@link org.apache.lucene.index.LeafReader#fields()}
* on the context's reader, for example.
*
* @param acceptDocs
* Bits that represent the allowable docs to match (typically deleted docs
* but possibly filtering other documents)
*
*
* @return a DocIdSet that provides the documents which should be permitted or
* prohibited in search results. <b>NOTE:</b> <code>null</code> should be returned if
* the filter doesn't accept any documents otherwise internal optimization might not apply
* in the case an <i>empty</i> {@link DocIdSet} is returned.
*/
public abstract DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException;
//
// Query compatibility
//
@Override
public boolean equals(Object that) {
// Query's default impl only compares boots but they do not matter in the
// case of filters since it does not influence scores
return this == that;
}
@Override
public int hashCode() {
// Query's default impl returns a hash of the boost but this is irrelevant to filters
return System.identityHashCode(this);
}
@Override
public String toString(String field) {
return getClass().getSimpleName();
}
@Override
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
return new Weight(this) {
@Override
public float getValueForNormalization() throws IOException {
return 0f;
}
@Override
public void normalize(float norm, float topLevelBoost) {}
@Override
public Explanation explain(LeafReaderContext context, int doc) throws IOException {
final Scorer scorer = scorer(context, context.reader().getLiveDocs());
final boolean match = (scorer != null && scorer.advance(doc) == doc);
final String desc;
if (match) {
assert scorer.score() == 0f;
desc = "Match on id " + doc;
} else {
desc = "No match on id " + doc;
}
return new ComplexExplanation(match, 0f, desc);
}
@Override
public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
final DocIdSet set = getDocIdSet(context, acceptDocs);
if (set == null) {
return null;
}
final DocIdSetIterator iterator = set.iterator();
if (iterator == null) {
return null;
}
return new Scorer(this) {
@Override
public float score() throws IOException {
return 0f;
}
@Override
public int freq() throws IOException {
return 1;
}
@Override
public int nextPosition() throws IOException {
return -1;
}
@Override
public int startOffset() throws IOException {
return -1;
}
@Override
public int endOffset() throws IOException {
return -1;
}
@Override
public BytesRef getPayload() throws IOException {
return null;
}
@Override
public int docID() {
return iterator.docID();
}
@Override
public int nextDoc() throws IOException {
return iterator.nextDoc();
}
@Override
public int advance(int target) throws IOException {
return iterator.advance(target);
}
@Override
public long cost() {
return iterator.cost();
}
};
}
};
}
}

View File

@ -586,6 +586,11 @@ public class LRUFilterCache implements FilterCache, Accountable {
public int hashCode() {
return in.hashCode() ^ getClass().hashCode();
}
@Override
public String toString(String field) {
return "CachingWrapperFilter(" + in.toString(field) + ")";
}
}
}

View File

@ -68,8 +68,8 @@ public class QueryWrapperFilter extends Filter {
}
@Override
public String toString() {
return "QueryWrapperFilter(" + query + ")";
public String toString(String field) {
return "QueryWrapperFilter(" + query.toString(field) + ")";
}
@Override

View File

@ -194,11 +194,11 @@ public final class DrillDownQuery extends Query {
static Filter getFilter(Query query) {
if (query instanceof ConstantScoreQuery) {
ConstantScoreQuery csq = (ConstantScoreQuery) query;
Filter filter = csq.getFilter();
if (filter != null) {
return filter;
Query sub = csq.getQuery();
if (sub instanceof Filter) {
return (Filter) sub;
} else {
return getFilter(csq.getQuery());
return getFilter(sub);
}
} else {
return null;

View File

@ -159,7 +159,7 @@ public class BooleanFilter extends Filter implements Iterable<FilterClause> {
/** Prints a user-readable version of this Filter. */
@Override
public String toString() {
public String toString(String field) {
final StringBuilder buffer = new StringBuilder("BooleanFilter(");
final int minLen = buffer.length();
for (final FilterClause c : clauses) {

View File

@ -74,9 +74,13 @@ public final class FilterClause {
return filter.hashCode() ^ occur.hashCode();
}
public String toString(String field) {
return occur.toString() + filter.toString(field);
}
@Override
public String toString() {
return occur.toString() + filter.toString();
return toString("");
}
}

View File

@ -244,7 +244,7 @@ public final class TermsFilter extends Filter implements Accountable {
}
@Override
public String toString() {
public String toString(String defaultField) {
StringBuilder builder = new StringBuilder();
BytesRef spare = new BytesRef(termsBytes);
boolean first = true;

View File

@ -132,11 +132,11 @@ public class PointVectorStrategy extends SpatialStrategy {
public Filter makeFilter(SpatialArgs args) {
//unwrap the CSQ from makeQuery
ConstantScoreQuery csq = makeQuery(args);
Filter filter = csq.getFilter();
if (filter != null)
return filter;
Query sub = csq.getQuery();
if (sub instanceof Filter)
return (Filter) sub;
else
return new QueryWrapperFilter(csq.getQuery());
return new QueryWrapperFilter(sub);
}
@Override

View File

@ -46,15 +46,16 @@ import org.apache.solr.common.SolrException;
* Experimental and subject to change.
*/
public class SolrConstantScoreQuery extends ConstantScoreQuery implements ExtendedQuery {
private final Filter filter;
boolean cache = true; // cache by default
int cost;
public SolrConstantScoreQuery(Filter filter) {
super(filter);
this.filter = filter;
}
/** Returns the encapsulated filter */
@Override
public Filter getFilter() {
return filter;
}