mirror of https://github.com/apache/lucene.git
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:
parent
0f7b913b07
commit
82eff4eb4d
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,79 +30,48 @@ 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);
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
private final Weight innerWeight;
|
||||
|
@ -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,38 +96,20 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
|
@ -166,9 +117,6 @@ public class ConstantScoreQuery extends Query {
|
|||
return new ConstantScoreScorer(scorer, queryWeight);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(LeafReaderContext context, int doc) throws IOException {
|
||||
final Scorer cs = scorer(context, context.reader().getLiveDocs());
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ public class FieldValueFilter extends Filter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public String toString(String defaultField) {
|
||||
return "FieldValueFilter [field=" + field + ", negate=" + negate + "]";
|
||||
}
|
||||
|
||||
|
|
|
@ -21,12 +21,13 @@ 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
|
||||
|
@ -56,4 +57,120 @@ public abstract class Filter {
|
|||
* 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();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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("");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue