LUCENE-6650: Spatial module no longer uses Filter.

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1706181 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David Wayne Smiley 2015-10-01 03:26:00 +00:00
parent 333cf6ea2e
commit c0bb4b8b35
28 changed files with 300 additions and 379 deletions

View File

@ -93,6 +93,10 @@ API Changes
* LUCENE-6489: The various span payload queries have been moved to the queries
submodule, and PayloadSpanUtil is now in sandbox. (Alan Woodward)
* LUCENE-6650: The spatial module no longer uses Filter in any way. All
spatial Filters are now subclass Query. The spatial heatmap/facet API
now accepts a Bits parameter to filter counts. (David Smiley, Adrien Grand)
Optimizations
* LUCENE-6708: TopFieldCollector does not compute the score several times on the

View File

@ -17,23 +17,21 @@ package org.apache.lucene.benchmark.byTask.feeds;
* limitations under the License.
*/
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.benchmark.byTask.utils.Config;
import org.apache.lucene.queries.CustomScoreQuery;
import org.apache.lucene.queries.function.FunctionQuery;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.benchmark.byTask.utils.Config;
import org.apache.lucene.queries.function.FunctionQuery;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
/**
* Reads spatial data from the body field docs from an internally created {@link LineDocSource}.
* It's parsed by {@link com.spatial4j.core.context.SpatialContext#readShapeFromWkt(String)} (String)} and then
@ -101,19 +99,16 @@ public class SpatialFileQueryMaker extends AbstractQueryMaker {
if (!Double.isNaN(distErrPct))
args.setDistErrPct(distErrPct);
Query filterQuery = strategy.makeQuery(args);
if (score) {
//wrap with distance computing query
ValueSource valueSource = strategy.makeDistanceValueSource(shape.getCenter());
return new CustomScoreQuery(strategy.makeQuery(args), new FunctionQuery(valueSource));
return new BooleanQuery.Builder()
.add(new FunctionQuery(valueSource), BooleanClause.Occur.MUST)//matches everything and provides score
.add(filterQuery, BooleanClause.Occur.FILTER)//filters (score isn't used)
.build();
} else {
//strategy.makeQuery() could potentially score (isn't well defined) so instead we call
// makeFilter() and wrap
Filter filter = strategy.makeFilter(args);
if (filter instanceof QueryWrapperFilter) {
return ((QueryWrapperFilter)filter).getQuery();
} else {
return new ConstantScoreQuery(filter);
}
return filterQuery; // assume constant scoring
}
}

View File

@ -24,8 +24,6 @@ import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.Field;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.queries.function.valuesource.ReciprocalFloatFunction;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.query.SpatialArgs;
@ -51,6 +49,8 @@ import org.apache.lucene.spatial.query.SpatialArgs;
* values of shapes, which is immaterial to indexing and search.
* <p>
* Thread-safe.
* <p>
* This API is marked as experimental, however it is quite stable.
*
* @lucene.experimental
*/
@ -118,32 +118,13 @@ public abstract class SpatialStrategy {
/**
* Make a Query based principally on {@link org.apache.lucene.spatial.query.SpatialOperation}
* and {@link Shape} from the supplied {@code args}.
* The default implementation is
* <pre>return new ConstantScoreQuery(makeFilter(args));</pre>
* and {@link Shape} from the supplied {@code args}. It should be constant scoring of 1.
*
* @throws UnsupportedOperationException If the strategy does not support the shape in {@code args}
* @throws org.apache.lucene.spatial.query.UnsupportedSpatialOperation If the strategy does not support the {@link
* org.apache.lucene.spatial.query.SpatialOperation} in {@code args}.
*/
public Query makeQuery(SpatialArgs args) {
return new ConstantScoreQuery(makeFilter(args));
}
/**
* Make a Filter based principally on {@link org.apache.lucene.spatial.query.SpatialOperation}
* and {@link Shape} from the supplied {@code args}.
* <p>
* If a subclasses implements
* {@link #makeQuery(org.apache.lucene.spatial.query.SpatialArgs)}
* then this method could be simply:
* <pre>return new QueryWrapperFilter(makeQuery(args).getQuery());</pre>
*
* @throws UnsupportedOperationException If the strategy does not support the shape in {@code args}
* @throws org.apache.lucene.spatial.query.UnsupportedSpatialOperation If the strategy does not support the {@link
* org.apache.lucene.spatial.query.SpatialOperation} in {@code args}.
*/
public abstract Filter makeFilter(SpatialArgs args);
public abstract Query makeQuery(SpatialArgs args);
/**
* Returns a ValueSource with values ranging from 1 to 0, depending inversely

View File

@ -17,21 +17,22 @@ package org.apache.lucene.spatial.bbox;
* limitations under the License.
*/
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.DoubleField;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.StringField;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.index.Term;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
@ -40,10 +41,6 @@ import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
import org.apache.lucene.spatial.util.DistanceToShapeValueSource;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.NumericUtils;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape;
/**
@ -206,25 +203,21 @@ public class BBoxStrategy extends SpatialStrategy {
}
//---------------------------------
// Query / Filter Building
// Query Building
//---------------------------------
@Override
public Filter makeFilter(SpatialArgs args) {
return new QueryWrapperFilter(makeSpatialQuery(args));
}
@Override
public ConstantScoreQuery makeQuery(SpatialArgs args) {
return new ConstantScoreQuery(makeSpatialQuery(args));
}
// Utility on SpatialStrategy?
// Utility on SpatialStrategy?
// public Query makeQueryWithValueSource(SpatialArgs args, ValueSource valueSource) {
// return new FilteredQuery(new FunctionQuery(valueSource), makeFilter(args));
// return new CustomScoreQuery(makeQuery(args), new FunctionQuery(valueSource));
//or...
// return new BooleanQuery.Builder()
// .add(new FunctionQuery(valueSource), BooleanClause.Occur.MUST)//matches everything and provides score
// .add(filterQuery, BooleanClause.Occur.FILTER)//filters (score isn't used)
// .build();
// }
private Query makeSpatialQuery(SpatialArgs args) {
@Override
public Query makeQuery(SpatialArgs args) {
Shape shape = args.getShape();
if (!(shape instanceof Rectangle))
throw new UnsupportedOperationException("Can only query by Rectangle, not " + shape);
@ -245,7 +238,7 @@ public class BBoxStrategy extends SpatialStrategy {
else { //no Overlaps support yet
throw new UnsupportedSpatialOperation(op);
}
return spatial;
return new ConstantScoreQuery(spatial);
}
/**

View File

@ -25,9 +25,7 @@ import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.Field;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
@ -144,10 +142,4 @@ public class CompositeSpatialStrategy extends SpatialStrategy {
}
}
@Override
public Filter makeFilter(SpatialArgs args) {
//note: Filters are being deprecated in LUCENE-6301
return new QueryWrapperFilter(makeQuery(args));
}
}

View File

@ -35,10 +35,9 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.TwoPhaseIterator;
import org.apache.lucene.search.Weight;
import org.apache.lucene.spatial.prefix.AbstractVisitingPrefixTreeFilter;
import org.apache.lucene.spatial.prefix.AbstractVisitingPrefixTreeQuery;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.DocIdSetBuilder;
/**
@ -50,13 +49,13 @@ import org.apache.lucene.util.DocIdSetBuilder;
*/
public class IntersectsRPTVerifyQuery extends Query {
private final IntersectsDifferentiatingFilter intersectsDiffFilter;
private final IntersectsDifferentiatingQuery intersectsDiffQuery;
private final ValueSource predicateValueSource; // we call FunctionValues.boolVal(doc)
public IntersectsRPTVerifyQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel,
int prefixGridScanLevel, ValueSource predicateValueSource) {
this.predicateValueSource = predicateValueSource;
this.intersectsDiffFilter = new IntersectsDifferentiatingFilter(queryShape, fieldName, grid, detailLevel,
this.intersectsDiffQuery = new IntersectsDifferentiatingQuery(queryShape, fieldName, grid, detailLevel,
prefixGridScanLevel);
}
@ -72,7 +71,7 @@ public class IntersectsRPTVerifyQuery extends Query {
IntersectsRPTVerifyQuery that = (IntersectsRPTVerifyQuery) o;
if (!intersectsDiffFilter.equals(that.intersectsDiffFilter)) return false;
if (!intersectsDiffQuery.equals(that.intersectsDiffQuery)) return false;
return predicateValueSource.equals(that.predicateValueSource);
}
@ -80,7 +79,7 @@ public class IntersectsRPTVerifyQuery extends Query {
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + intersectsDiffFilter.hashCode();
result = 31 * result + intersectsDiffQuery.hashCode();
result = 31 * result + predicateValueSource.hashCode();
return result;
}
@ -93,8 +92,8 @@ public class IntersectsRPTVerifyQuery extends Query {
@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
// Compute approx & exact
final IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor result =
intersectsDiffFilter.compute(context, null);
final IntersectsDifferentiatingQuery.IntersectsDifferentiatingVisitor result =
intersectsDiffQuery.compute(context);
if (result.approxDocIdSet == null) {
return null;
}
@ -138,24 +137,26 @@ public class IntersectsRPTVerifyQuery extends Query {
};
}
//This is a "Filter" but we don't use it as-such; the caller calls the constructor and then compute() and examines
//This may be a "Query" but we don't use it as-such; the caller calls the constructor and then compute() and examines
// the results which consists of two parts -- the approximated results, and a subset of exact matches. The
// difference needs to be verified.
// TODO refactor AVPTF to not be a Query/Filter?
private static class IntersectsDifferentiatingFilter extends AbstractVisitingPrefixTreeFilter {
// TODO refactor AVPTQ to not be a Query?
private static class IntersectsDifferentiatingQuery extends AbstractVisitingPrefixTreeQuery {
public IntersectsDifferentiatingFilter(Shape queryShape, String fieldName, SpatialPrefixTree grid,
int detailLevel, int prefixGridScanLevel) {
public IntersectsDifferentiatingQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid,
int detailLevel, int prefixGridScanLevel) {
super(queryShape, fieldName, grid, detailLevel, prefixGridScanLevel);
}
IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor compute(LeafReaderContext context, Bits acceptDocs) throws IOException {
final IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor result = new IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor(context, acceptDocs);
IntersectsDifferentiatingQuery.IntersectsDifferentiatingVisitor compute(LeafReaderContext context)
throws IOException {
final IntersectsDifferentiatingQuery.IntersectsDifferentiatingVisitor result =
new IntersectsDifferentiatingQuery.IntersectsDifferentiatingVisitor(context);
result.getDocIdSet();//computes
return result;
}
// TODO consider if IntersectsPrefixTreeFilter should simply do this and provide both sets
// TODO consider if IntersectsPrefixTreeQuery should simply do this and provide both sets
class IntersectsDifferentiatingVisitor extends VisitorTemplate {
DocIdSetBuilder approxBuilder = new DocIdSetBuilder(maxDoc);
@ -165,8 +166,8 @@ public class IntersectsRPTVerifyQuery extends Query {
DocIdSet exactDocIdSet;
DocIdSet approxDocIdSet;
public IntersectsDifferentiatingVisitor(LeafReaderContext context, Bits acceptDocs) throws IOException {
super(context, acceptDocs);
public IntersectsDifferentiatingVisitor(LeafReaderContext context) throws IOException {
super(context);
}
@Override
@ -188,7 +189,7 @@ public class IntersectsRPTVerifyQuery extends Query {
}
approxDocIdSet = approxBuilder.build();
}
return null;//unused in this weird re-use of AVPTF
return null;//unused in this weird re-use of AVPTQ
}
@Override
@ -218,7 +219,7 @@ public class IntersectsRPTVerifyQuery extends Query {
}
@Override
public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException {
public DocIdSet getDocIdSet(LeafReaderContext context) throws IOException {
throw new IllegalStateException();
}

View File

@ -20,31 +20,35 @@ package org.apache.lucene.spatial.prefix;
import java.io.IOException;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.index.FilterLeafReader.FilterPostingsEnum;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.ConstantScoreScorer;
import org.apache.lucene.search.ConstantScoreWeight;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.DocIdSetBuilder;
/**
* Base class for Lucene Filters on SpatialPrefixTree fields.
* @lucene.experimental
* Base class for Lucene Queries on SpatialPrefixTree fields.
* @lucene.internal
*/
public abstract class AbstractPrefixTreeFilter extends Filter {
public abstract class AbstractPrefixTreeQuery extends Query {
protected final Shape queryShape;
protected final String fieldName;
protected final SpatialPrefixTree grid;//not in equals/hashCode since it's implied for a specific field
protected final int detailLevel;
public AbstractPrefixTreeFilter(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel) {
public AbstractPrefixTreeQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel) {
this.queryShape = queryShape;
this.fieldName = fieldName;
this.grid = grid;
@ -56,7 +60,7 @@ public abstract class AbstractPrefixTreeFilter extends Filter {
if (this == o) return true;
if (super.equals(o) == false) return false;
AbstractPrefixTreeFilter that = (AbstractPrefixTreeFilter) o;
AbstractPrefixTreeQuery that = (AbstractPrefixTreeQuery) o;
if (detailLevel != that.detailLevel) return false;
if (!fieldName.equals(that.fieldName)) return false;
@ -74,22 +78,40 @@ public abstract class AbstractPrefixTreeFilter extends Filter {
return result;
}
@Override
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
return new ConstantScoreWeight(this) {
@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
DocIdSet docSet = getDocIdSet(context);
if (docSet == null) {
return null;
}
DocIdSetIterator disi = docSet.iterator();
if (disi == null) {
return null;
}
return new ConstantScoreScorer(this, score(), disi);
}
};
}
protected abstract DocIdSet getDocIdSet(LeafReaderContext context) throws IOException;
/** Holds transient state and docid collecting utility methods as part of
* traversing a {@link TermsEnum} for a {@link org.apache.lucene.index.LeafReaderContext}. */
public abstract class BaseTermsEnumTraverser {//TODO rename to LeafTermsEnumTraverser ?
//note: only 'fieldName' (accessed in constructor) keeps this from being a static inner class
protected final LeafReaderContext context;
protected Bits acceptDocs;
protected final int maxDoc;
protected TermsEnum termsEnum;//remember to check for null!
protected PostingsEnum postingsEnum;
public BaseTermsEnumTraverser(LeafReaderContext context, Bits acceptDocs) throws IOException {
public BaseTermsEnumTraverser(LeafReaderContext context) throws IOException {
this.context = context;
LeafReader reader = context.reader();
this.acceptDocs = acceptDocs;
this.maxDoc = reader.maxDoc();
Terms terms = reader.terms(fieldName);
if (terms != null)
@ -99,50 +121,14 @@ public abstract class AbstractPrefixTreeFilter extends Filter {
protected void collectDocs(BitSet bitSet) throws IOException {
assert termsEnum != null;
postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.NONE);
bitSet.or(wrap(postingsEnum, acceptDocs));
bitSet.or(postingsEnum);
}
protected void collectDocs(DocIdSetBuilder docSetBuilder) throws IOException {
assert termsEnum != null;
postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.NONE);
docSetBuilder.add(wrap(postingsEnum, acceptDocs));
docSetBuilder.add(postingsEnum);
}
}
/** Filter the given {@link PostingsEnum} with the given {@link Bits}. */
private static PostingsEnum wrap(PostingsEnum iterator, Bits acceptDocs) {
if (iterator == null || acceptDocs == null) {
return iterator;
}
return new BitsFilteredPostingsEnum(iterator, acceptDocs);
}
/** A {@link PostingsEnum} which is filtered by some random-access bits. */
private static class BitsFilteredPostingsEnum extends FilterPostingsEnum {
private final Bits bits;
private BitsFilteredPostingsEnum(PostingsEnum in, Bits bits) {
super(in);
this.bits = bits;
}
private int doNext(int doc) throws IOException {
while (doc != NO_MORE_DOCS && bits.get(doc) == false) {
doc = in.nextDoc();
}
return doc;
}
@Override
public int nextDoc() throws IOException {
return doNext(in.nextDoc());
}
@Override
public int advance(int target) throws IOException {
return doNext(in.advance(target));
}
}
}

View File

@ -28,7 +28,6 @@ import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.CellIterator;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
/**
@ -36,27 +35,26 @@ import org.apache.lucene.util.BytesRef;
* visitor design patterns for subclasses to guide the traversal and collect
* matching documents.
* <p>
* Subclasses implement {@link #getDocIdSet(org.apache.lucene.index.LeafReaderContext,
* org.apache.lucene.util.Bits)} by instantiating a custom {@link
* VisitorTemplate} subclass (i.e. an anonymous inner class) and implement the
* required methods.
* Subclasses implement {@link #getDocIdSet(org.apache.lucene.index.LeafReaderContext)}
* by instantiating a custom {@link VisitorTemplate} subclass (i.e. an anonymous inner class)
* and implement the required methods.
*
* @lucene.internal
*/
public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTreeFilter {
public abstract class AbstractVisitingPrefixTreeQuery extends AbstractPrefixTreeQuery {
//Historical note: this code resulted from a refactoring of RecursivePrefixTreeFilter,
//Historical note: this code resulted from a refactoring of RecursivePrefixTreeQuery,
// which in turn came out of SOLR-2155
//This class perhaps could have been implemented in terms of FilteredTermsEnum & MultiTermQuery
// & MultiTermQueryWrapperFilter. Maybe so for simple Intersects predicate but not for when we want to collect terms
//This class perhaps could have been implemented in terms of FilteredTermsEnum & MultiTermQuery.
// Maybe so for simple Intersects predicate but not for when we want to collect terms
// differently depending on cell state like IsWithin and for fuzzy/accurate collection planned improvements. At
// least it would just make things more complicated.
protected final int prefixGridScanLevel;//at least one less than grid.getMaxLevels()
public AbstractVisitingPrefixTreeFilter(Shape queryShape, String fieldName, SpatialPrefixTree grid,
int detailLevel, int prefixGridScanLevel) {
public AbstractVisitingPrefixTreeQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid,
int detailLevel, int prefixGridScanLevel) {
super(queryShape, fieldName, grid, detailLevel);
this.prefixGridScanLevel = Math.max(0, Math.min(prefixGridScanLevel, grid.getMaxLevels() - 1));
assert detailLevel <= grid.getMaxLevels();
@ -66,8 +64,7 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
* An abstract class designed to make it easy to implement predicates or
* other operations on a {@link SpatialPrefixTree} indexed field. An instance
* of this class is not designed to be re-used across LeafReaderContext
* instances so simply create a new one for each call to, say a {@link
* org.apache.lucene.search.Filter#getDocIdSet(org.apache.lucene.index.LeafReaderContext, org.apache.lucene.util.Bits)}.
* instances so simply create a new one per-leaf.
* The {@link #getDocIdSet()} method here starts the work. It first checks
* that there are indexed terms; if not it quickly returns null. Then it calls
* {@link #start()} so a subclass can set up a return value, like an
@ -112,8 +109,8 @@ public abstract class AbstractVisitingPrefixTreeFilter extends AbstractPrefixTre
private BytesRef thisTerm;//the result of termsEnum.term()
private Cell indexedCell;//Cell wrapper of thisTerm. Always updated when thisTerm is.
public VisitorTemplate(LeafReaderContext context, Bits acceptDocs) throws IOException {
super(context, acceptDocs);
public VisitorTemplate(LeafReaderContext context) throws IOException {
super(context);
}
public DocIdSet getDocIdSet() throws IOException {

View File

@ -41,7 +41,7 @@ import org.apache.lucene.util.SentinelIntSet;
*
* @lucene.experimental
*/
public class ContainsPrefixTreeFilter extends AbstractPrefixTreeFilter {
public class ContainsPrefixTreeQuery extends AbstractPrefixTreeQuery {
/**
* If the spatial data for a document is comprised of multiple overlapping or adjacent parts,
@ -52,7 +52,7 @@ public class ContainsPrefixTreeFilter extends AbstractPrefixTreeFilter {
*/
protected final boolean multiOverlappingIndexedShapes;
public ContainsPrefixTreeFilter(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel, boolean multiOverlappingIndexedShapes) {
public ContainsPrefixTreeQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel, boolean multiOverlappingIndexedShapes) {
super(queryShape, fieldName, grid, detailLevel);
this.multiOverlappingIndexedShapes = multiOverlappingIndexedShapes;
}
@ -61,7 +61,7 @@ public class ContainsPrefixTreeFilter extends AbstractPrefixTreeFilter {
public boolean equals(Object o) {
if (!super.equals(o))
return false;
return multiOverlappingIndexedShapes == ((ContainsPrefixTreeFilter)o).multiOverlappingIndexedShapes;
return multiOverlappingIndexedShapes == ((ContainsPrefixTreeQuery)o).multiOverlappingIndexedShapes;
}
@Override
@ -71,7 +71,7 @@ public class ContainsPrefixTreeFilter extends AbstractPrefixTreeFilter {
@Override
public String toString(String field) {
return "ContainsPrefixTreeFilter(" +
return getClass().getSimpleName() + "(" +
"fieldName=" + fieldName + "," +
"queryShape=" + queryShape + "," +
"detailLevel=" + detailLevel + "," +
@ -80,14 +80,14 @@ public class ContainsPrefixTreeFilter extends AbstractPrefixTreeFilter {
}
@Override
public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException {
return new ContainsVisitor(context, acceptDocs).visit(grid.getWorldCell(), acceptDocs);
protected DocIdSet getDocIdSet(LeafReaderContext context) throws IOException {
return new ContainsVisitor(context).visit(grid.getWorldCell(), null);
}
private class ContainsVisitor extends BaseTermsEnumTraverser {
public ContainsVisitor(LeafReaderContext context, Bits acceptDocs) throws IOException {
super(context, acceptDocs);
public ContainsVisitor(LeafReaderContext context) throws IOException {
super(context);
if (termsEnum != null) {
nextTerm();//advance to first
}
@ -239,6 +239,7 @@ public class ContainsPrefixTreeFilter extends AbstractPrefixTreeFilter {
/** A hash based mutable set of docIds. If this were Solr code then we might
* use a combination of HashDocSet and SortedIntDocSet instead. */
// TODO use DocIdSetBuilder?
private static class SmallDocSet extends DocIdSet implements Bits {
private final SentinelIntSet intSet;

View File

@ -27,15 +27,17 @@ import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.SpatialRelation;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.CellIterator;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.Bits;
/**
* Computes spatial facets in two dimensions as a grid of numbers. The data is often visualized as a so-called
* "heatmap", hence the name.
*
* @lucene.experimental
*/
public class HeatmapFacetCounter {
//TODO where should this code live? It could go to PrefixTreeFacetCounter, or maybe here in its own class is fine.
@ -78,19 +80,16 @@ public class HeatmapFacetCounter {
* uses when approximating what level to go to when indexing a shape given a distErrPct.
*
* @param context the IndexReader's context
* @param filter a Filter to limit counted docs. For optimal performance, it's
* {@link org.apache.lucene.search.DocIdSet#bits()} should be non-null. If no filter is provided, live
* docs are counted.
* @param inputShape the shape to gather grid squares for; typically a {@link com.spatial4j.core.shape.Rectangle}.
* @param topAcceptDocs a Bits to limit counted docs. If null, live docs are counted.
* @param inputShape the shape to gather grid squares for; typically a {@link Rectangle}.
* The <em>actual</em> heatmap area will usually be larger since the cells on the edge that overlap
* are returned. We always return a rectangle of integers even if the inputShape isn't a rectangle
* -- the non-intersecting cells will all be 0.
* If null is given, the entire world is assumed.
* @param facetLevel the target depth (detail) of cells.
* @param maxCells the maximum number of cells to return. If the cells exceed this count, an
* IllegalArgumentException is thrown.
*/
public static Heatmap calcFacets(PrefixTreeStrategy strategy, IndexReaderContext context, Filter filter,
public static Heatmap calcFacets(PrefixTreeStrategy strategy, IndexReaderContext context, Bits topAcceptDocs,
Shape inputShape, final int facetLevel, int maxCells) throws IOException {
if (maxCells > (MAX_ROWS_OR_COLUMNS * MAX_ROWS_OR_COLUMNS)) {
throw new IllegalArgumentException("maxCells (" + maxCells + ") should be <= " + MAX_ROWS_OR_COLUMNS);
@ -153,7 +152,7 @@ public class HeatmapFacetCounter {
Map<Rectangle,Integer> ancestors = new HashMap<>();
//Now lets count some facets!
PrefixTreeFacetCounter.compute(strategy, context, filter, inputShape, facetLevel,
PrefixTreeFacetCounter.compute(strategy, context, topAcceptDocs, inputShape, facetLevel,
new PrefixTreeFacetCounter.FacetVisitor() {
@Override
public void visit(Cell cell, int count) {

View File

@ -26,25 +26,24 @@ import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.BitDocIdSet;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
/**
* A Filter matching documents that have an {@link SpatialRelation#INTERSECTS}
* A Query matching documents that have an {@link SpatialRelation#INTERSECTS}
* (i.e. not DISTINCT) relationship with a provided query shape.
*
* @lucene.internal
*/
public class IntersectsPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
public class IntersectsPrefixTreeQuery extends AbstractVisitingPrefixTreeQuery {
public IntersectsPrefixTreeFilter(Shape queryShape, String fieldName,
SpatialPrefixTree grid, int detailLevel,
int prefixGridScanLevel) {
public IntersectsPrefixTreeQuery(Shape queryShape, String fieldName,
SpatialPrefixTree grid, int detailLevel,
int prefixGridScanLevel) {
super(queryShape, fieldName, grid, detailLevel, prefixGridScanLevel);
}
@Override
public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException {
protected DocIdSet getDocIdSet(LeafReaderContext context) throws IOException {
/* Possible optimizations (in IN ADDITION TO THOSE LISTED IN VISITORTEMPLATE):
* If docFreq is 1 (or < than some small threshold), then check to see if we've already
@ -54,7 +53,7 @@ public class IntersectsPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter
* Point query shape optimization when the only indexed data is a point (no leaves). Result is a term query.
*/
return new VisitorTemplate(context, acceptDocs) {
return new VisitorTemplate(context) {
private FixedBitSet results;
@Override
@ -86,7 +85,7 @@ public class IntersectsPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter
@Override
public String toString(String field) {
return "IntersectsPrefixTreeFilter(" +
return getClass().getSimpleName() + "(" +
"fieldName=" + fieldName + "," +
"queryShape=" + queryShape + "," +
"detailLevel=" + detailLevel + "," +

View File

@ -28,9 +28,9 @@ import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree;
import org.apache.lucene.util.Bits;
import static org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree.UnitNRShape;
@ -72,13 +72,13 @@ public class NumberRangePrefixTreeStrategy extends RecursivePrefixTreeStrategy {
/** Calculates facets between {@code start} and {@code end} to a detail level one greater than that provided by the
* arguments. For example providing March to October of 2014 would return facets to the day level of those months.
* This is just a convenience method.
* @see #calcFacets(IndexReaderContext, Filter, Shape, int)
* @see #calcFacets(IndexReaderContext, Bits, Shape, int)
*/
public Facets calcFacets(IndexReaderContext context, Filter filter, UnitNRShape start, UnitNRShape end)
public Facets calcFacets(IndexReaderContext context, Bits topAcceptDocs, UnitNRShape start, UnitNRShape end)
throws IOException {
Shape facetRange = getGrid().toRangeShape(start, end);
int detailLevel = Math.max(start.getLevel(), end.getLevel()) + 1;
return calcFacets(context, filter, facetRange, detailLevel);
return calcFacets(context, topAcceptDocs, facetRange, detailLevel);
}
/**
@ -88,10 +88,10 @@ public class NumberRangePrefixTreeStrategy extends RecursivePrefixTreeStrategy {
* {@link org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree.UnitNRShape#getLevel()}.
* Facet computation is implemented by navigating the underlying indexed terms efficiently.
*/
public Facets calcFacets(IndexReaderContext context, Filter filter, Shape facetRange, final int level)
public Facets calcFacets(IndexReaderContext context, Bits topAcceptDocs, Shape facetRange, final int level)
throws IOException {
final Facets facets = new Facets(level);
PrefixTreeFacetCounter.compute(this, context, filter, facetRange, level,
PrefixTreeFacetCounter.compute(this, context, topAcceptDocs, facetRange, level,
new PrefixTreeFacetCounter.FacetVisitor() {
Facets.FacetParentVal parentFacet;
UnitNRShape parentShape;

View File

@ -25,11 +25,9 @@ import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.SparseFixedBitSet;
/**
* Computes facets on cells for {@link org.apache.lucene.spatial.prefix.PrefixTreeStrategy}.
@ -67,41 +65,34 @@ public class PrefixTreeFacetCounter {
/**
* Computes facets using a callback/visitor style design, allowing flexibility for the caller to determine what to do
* with each underlying count.
*
* @param strategy the prefix tree strategy (contains the field reference, grid, max levels)
* @param context the IndexReader's context
* @param filter a Filter to limit counted docs. For optimal performance, it's
* {@link org.apache.lucene.search.DocIdSet#bits()} should be non-null. If no filter is provided, live
* docs are counted.
* @param topAcceptDocs a Bits to limit counted docs. If null, live docs are counted.
* @param queryShape the shape to limit the range of facet counts to
* @param facetLevel the maximum depth (detail) of faceted cells
* @param facetVisitor the visitor/callback to receive the counts
*/
public static void compute(PrefixTreeStrategy strategy, IndexReaderContext context, Filter filter,
public static void compute(PrefixTreeStrategy strategy, IndexReaderContext context, Bits topAcceptDocs,
Shape queryShape, int facetLevel, FacetVisitor facetVisitor)
throws IOException {
//We collect per-leaf
for (final LeafReaderContext leafCtx : context.leaves()) {
//determine leaf acceptDocs Bits
Bits leafAcceptDocs;
if (filter == null) {
if (topAcceptDocs == null) {
leafAcceptDocs = leafCtx.reader().getLiveDocs();//filter deleted
} else {
final DocIdSet docIdSet = filter.getDocIdSet(leafCtx, leafCtx.reader().getLiveDocs());
if (docIdSet == null) {
continue;//no docs in filter
}
leafAcceptDocs = docIdSet.bits();
if (leafAcceptDocs == null) {
final DocIdSetIterator iterator = docIdSet.iterator();
if (iterator == null) {
continue;//no docs in filter
leafAcceptDocs = new Bits() {
@Override
public boolean get(int index) {
return topAcceptDocs.get(leafCtx.docBase + index);
}
//build bits from iterator (abnormal, hopefully, not expecting many docs)
SparseFixedBitSet bitSet = new SparseFixedBitSet(leafCtx.reader().maxDoc());
bitSet.or(iterator);
leafAcceptDocs = bitSet;
}
@Override
public int length() {
return leafCtx.reader().maxDoc();
}
};
}
compute(strategy, leafCtx, leafAcceptDocs, queryShape, facetLevel, facetVisitor);
@ -122,21 +113,20 @@ public class PrefixTreeFacetCounter {
// another scanLevel would be much faster and it tends to be a risky knob (can help a little, can hurt a ton).
// TODO use RPT's configured scan level? Do we know better here? Hard to say.
final int scanLevel = tree.getMaxLevels();
//AbstractVisitingPrefixTreeFilter is a Lucene Filter. We don't need a filter; we use it for its great prefix-tree
// traversal code. TODO consider refactoring if/when it makes sense (more use cases than this)
new AbstractVisitingPrefixTreeFilter(queryShape, strategy.getFieldName(), tree, facetLevel, scanLevel) {
new AbstractVisitingPrefixTreeQuery(queryShape, strategy.getFieldName(), tree, facetLevel, scanLevel) {
@Override
public String toString(String field) {
return "anonPrefixTreeFilter";
return "anonPrefixTreeQuery";//un-used
}
@Override
public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException {
public DocIdSet getDocIdSet(LeafReaderContext contexts) throws IOException {
assert facetLevel == super.detailLevel;//same thing, FYI. (constant)
return new VisitorTemplate(context, acceptDocs) {
return new VisitorTemplate(context) {
@Override
protected void start() throws IOException {
@ -185,7 +175,7 @@ public class PrefixTreeFacetCounter {
int count = 0;
postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.NONE);
while (postingsEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
if (acceptDocs != null && acceptDocs.get(postingsEnum.docID()) == false) {
if (acceptDocs.get(postingsEnum.docID()) == false) {
continue;
}
count++;
@ -207,6 +197,6 @@ public class PrefixTreeFacetCounter {
}.getDocIdSet();
}
}.getDocIdSet(context, acceptDocs);
}.getDocIdSet(context);
}
}

View File

@ -29,12 +29,12 @@ import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.util.ShapeFieldCacheDistanceValueSource;
import org.apache.lucene.util.Bits;
/**
* An abstract SpatialStrategy based on {@link SpatialPrefixTree}. The two
@ -75,7 +75,7 @@ import org.apache.lucene.spatial.util.ShapeFieldCacheDistanceValueSource;
* configuration item is {@link #setDistErrPct(double)} which balances
* shape precision against scalability. See those javadocs.
*
* @lucene.internal
* @lucene.experimental
*/
public abstract class PrefixTreeStrategy extends SpatialStrategy {
protected final SpatialPrefixTree grid;
@ -200,10 +200,10 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
* Computes spatial facets in two dimensions as a grid of numbers. The data is often visualized as a so-called
* "heatmap".
*
* @see org.apache.lucene.spatial.prefix.HeatmapFacetCounter#calcFacets(PrefixTreeStrategy, org.apache.lucene.index.IndexReaderContext, org.apache.lucene.search.Filter, com.spatial4j.core.shape.Shape, int, int)
* @see HeatmapFacetCounter#calcFacets(PrefixTreeStrategy, IndexReaderContext, Bits, Shape, int, int)
*/
public HeatmapFacetCounter.Heatmap calcFacets(IndexReaderContext context, Filter filter,
public HeatmapFacetCounter.Heatmap calcFacets(IndexReaderContext context, Bits topAcceptDocs,
Shape inputShape, final int facetLevel, int maxCells) throws IOException {
return HeatmapFacetCounter.calcFacets(this, context, filter, inputShape, facetLevel, maxCells);
return HeatmapFacetCounter.calcFacets(this, context, topAcceptDocs, inputShape, facetLevel, maxCells);
}
}

View File

@ -23,7 +23,7 @@ import java.util.List;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.CellIterator;
import org.apache.lucene.spatial.prefix.tree.LegacyCell;
@ -33,7 +33,7 @@ import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
/**
* A {@link PrefixTreeStrategy} which uses {@link AbstractVisitingPrefixTreeFilter}.
* A {@link PrefixTreeStrategy} which uses {@link AbstractVisitingPrefixTreeQuery}.
* This strategy has support for searching non-point shapes (note: not tested).
* Even a query shape with distErrPct=0 (fully precise to the grid) should have
* good performance for typical data, unless there is a lot of indexed data
@ -83,7 +83,7 @@ public class RecursivePrefixTreeStrategy extends PrefixTreeStrategy {
return multiOverlappingIndexedShapes;
}
/** See {@link ContainsPrefixTreeFilter#multiOverlappingIndexedShapes}. */
/** See {@link ContainsPrefixTreeQuery#multiOverlappingIndexedShapes}. */
public void setMultiOverlappingIndexedShapes(boolean multiOverlappingIndexedShapes) {
this.multiOverlappingIndexedShapes = multiOverlappingIndexedShapes;
}
@ -171,21 +171,21 @@ public class RecursivePrefixTreeStrategy extends PrefixTreeStrategy {
}
@Override
public Filter makeFilter(SpatialArgs args) {
public Query makeQuery(SpatialArgs args) {
final SpatialOperation op = args.getOperation();
Shape shape = args.getShape();
int detailLevel = grid.getLevelForDistance(args.resolveDistErr(ctx, distErrPct));
if (op == SpatialOperation.Intersects) {
return new IntersectsPrefixTreeFilter(
return new IntersectsPrefixTreeQuery(
shape, getFieldName(), grid, detailLevel, prefixGridScanLevel);
} else if (op == SpatialOperation.IsWithin) {
return new WithinPrefixTreeFilter(
return new WithinPrefixTreeQuery(
shape, getFieldName(), grid, detailLevel, prefixGridScanLevel,
-1);//-1 flag is slower but ensures correct results
} else if (op == SpatialOperation.Contains) {
return new ContainsPrefixTreeFilter(shape, getFieldName(), grid, detailLevel,
return new ContainsPrefixTreeQuery(shape, getFieldName(), grid, detailLevel,
multiOverlappingIndexedShapes);
}
throw new UnsupportedSpatialOperation(op);

View File

@ -23,8 +23,7 @@ import java.util.List;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.queries.TermsQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.CellIterator;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
@ -69,7 +68,7 @@ public class TermQueryPrefixTreeStrategy extends PrefixTreeStrategy {
}
@Override
public Filter makeFilter(SpatialArgs args) {
public Query makeQuery(SpatialArgs args) {
final SpatialOperation op = args.getOperation();
if (op != SpatialOperation.Intersects)
throw new UnsupportedSpatialOperation(op);
@ -107,7 +106,7 @@ public class TermQueryPrefixTreeStrategy extends PrefixTreeStrategy {
}
//unfortunately TermsQuery will needlessly sort & dedupe
//TODO an automatonQuery might be faster?
return new QueryWrapperFilter(new TermsQuery(getFieldName(), terms));
return new TermsQuery(getFieldName(), terms);
}
}

View File

@ -49,7 +49,7 @@ import org.apache.lucene.util.FixedBitSet;
*
* @lucene.experimental
*/
public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
public class WithinPrefixTreeQuery extends AbstractVisitingPrefixTreeQuery {
//TODO LUCENE-4869: implement faster algorithm based on filtering out false-positives of a
// minimal query buffer by looking in a DocValues cache holding a representative
// point of each disjoint component of a document's shape(s).
@ -60,14 +60,14 @@ public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
private final Shape bufferedQueryShape;//if null then the whole world
/**
* See {@link AbstractVisitingPrefixTreeFilter#AbstractVisitingPrefixTreeFilter(com.spatial4j.core.shape.Shape, String, org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree, int, int)}.
* See {@link AbstractVisitingPrefixTreeQuery#AbstractVisitingPrefixTreeQuery(com.spatial4j.core.shape.Shape, String, org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree, int, int)}.
* {@code queryBuffer} is the (minimum) distance beyond the query shape edge
* where non-matching documents are looked for so they can be excluded. If
* -1 is used then the whole world is examined (a good default for correctness).
*/
public WithinPrefixTreeFilter(Shape queryShape, String fieldName, SpatialPrefixTree grid,
int detailLevel, int prefixGridScanLevel,
double queryBuffer) {
public WithinPrefixTreeQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid,
int detailLevel, int prefixGridScanLevel,
double queryBuffer) {
super(queryShape, fieldName, grid, detailLevel, prefixGridScanLevel);
this.bufferedQueryShape = queryBuffer == -1 ? null : bufferShape(queryShape, queryBuffer);
}
@ -76,7 +76,7 @@ public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
public boolean equals(Object o) {
if (!super.equals(o)) return false;//checks getClass == o.getClass & instanceof
WithinPrefixTreeFilter that = (WithinPrefixTreeFilter) o;
WithinPrefixTreeQuery that = (WithinPrefixTreeQuery) o;
if (bufferedQueryShape != null ? !bufferedQueryShape.equals(that.bufferedQueryShape) : that.bufferedQueryShape != null)
return false;
@ -93,7 +93,7 @@ public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
@Override
public String toString(String field) {
return "WithinPrefixTreeFilter(" +
return getClass().getSimpleName() + "(" +
"fieldName=" + fieldName + "," +
"queryShape=" + queryShape + "," +
"detailLevel=" + detailLevel + "," +
@ -147,8 +147,8 @@ public class WithinPrefixTreeFilter extends AbstractVisitingPrefixTreeFilter {
@Override
public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException {
return new VisitorTemplate(context, acceptDocs) {
protected DocIdSet getDocIdSet(LeafReaderContext context) throws IOException {
return new VisitorTemplate(context) {
private FixedBitSet inside;
private FixedBitSet outside;

View File

@ -17,30 +17,6 @@ package org.apache.lucene.spatial.serialized;
* limitations under the License.
*/
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.io.BinaryCodec;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.BinaryDocValuesField;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.util.DistanceToShapeValueSource;
import org.apache.lucene.spatial.util.ShapePredicateValueSource;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@ -49,6 +25,30 @@ import java.io.FilterOutputStream;
import java.io.IOException;
import java.util.Map;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.io.BinaryCodec;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.BinaryDocValuesField;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.RandomAccessWeight;
import org.apache.lucene.search.TwoPhaseIterator;
import org.apache.lucene.search.Weight;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.util.DistanceToShapeValueSource;
import org.apache.lucene.spatial.util.ShapePredicateValueSource;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
/**
* A SpatialStrategy based on serializing a Shape stored into BinaryDocValues.
@ -104,23 +104,16 @@ public class SerializedDVStrategy extends SpatialStrategy {
return new DistanceToShapeValueSource(makeShapeValueSource(), queryPoint, multiplier, ctx);
}
@Override
public Query makeQuery(SpatialArgs args) {
throw new UnsupportedOperationException("This strategy can't return a query that operates" +
" efficiently. Instead try a Filter or ValueSource.");
}
/**
* Returns a Filter that should be used in a random-access fashion.
* Use in another manner is likely to result in an {@link java.lang.UnsupportedOperationException}
* to prevent misuse because the filter can't efficiently work via iteration.
* Returns a Query that should be used in a random-access fashion.
* Use in another manner will be SLOW.
*/
@Override
public Filter makeFilter(final SpatialArgs args) {
public Query makeQuery(SpatialArgs args) {
ValueSource shapeValueSource = makeShapeValueSource();
ShapePredicateValueSource predicateValueSource = new ShapePredicateValueSource(
shapeValueSource, args.getOperation(), args.getShape());
return new PredicateValueSourceFilter(predicateValueSource);
return new PredicateValueSourceQuery(predicateValueSource);
}
/**
@ -132,38 +125,25 @@ public class SerializedDVStrategy extends SpatialStrategy {
return new ShapeDocValueSource(getFieldName(), ctx.getBinaryCodec());
}
/** This filter only supports returning a DocSet with a bits(). If you try to grab the
* iterator then you'll get an UnsupportedOperationException.
/** Warning: don't iterate over the results of this query; it's designed for use in a random-access fashion
* by {@link TwoPhaseIterator}.
*/
static class PredicateValueSourceFilter extends Filter {
static class PredicateValueSourceQuery extends Query {
private final ValueSource predicateValueSource;//we call boolVal(doc)
public PredicateValueSourceFilter(ValueSource predicateValueSource) {
super(true);
public PredicateValueSourceQuery(ValueSource predicateValueSource) {
this.predicateValueSource = predicateValueSource;
}
@Override
public DocIdSet getDocIdSet(final LeafReaderContext context, final Bits acceptDocs) throws IOException {
return new DocIdSet() {
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
return new RandomAccessWeight(this) {
@Override
public DocIdSetIterator iterator() throws IOException {
throw new UnsupportedOperationException(
"Iteration is too slow; consume using DocIdSet.bits() instead");
//Note that if you're truly bent on doing this, then see FunctionValues.getRangeScorer
}
@Override
public Bits bits() throws IOException {
//null Map context -- we simply don't have one. That's ok.
protected Bits getMatchingDocs(LeafReaderContext context) throws IOException {
final FunctionValues predFuncValues = predicateValueSource.getValues(null, context);
return new Bits() {
@Override
public boolean get(int index) {
if (acceptDocs != null && !acceptDocs.get(index))
return false;
return predFuncValues.boolVal(index);
}
@ -173,11 +153,6 @@ public class SerializedDVStrategy extends SpatialStrategy {
}
};
}
@Override
public long ramBytesUsed() {
return 0L;
}
};
}
@ -186,7 +161,7 @@ public class SerializedDVStrategy extends SpatialStrategy {
if (this == o) return true;
if (super.equals(o) == false) return false;
PredicateValueSourceFilter that = (PredicateValueSourceFilter) o;
PredicateValueSourceQuery that = (PredicateValueSourceQuery) o;
if (!predicateValueSource.equals(that.predicateValueSource)) return false;
@ -200,11 +175,11 @@ public class SerializedDVStrategy extends SpatialStrategy {
@Override
public String toString(String field) {
return "PredicateValueSourceFilter(" +
return "PredicateValueSourceQuery(" +
predicateValueSource.toString() +
")";
}
}//PredicateValueSourceFilter
}//PredicateValueSourceQuery
/**
* Implements a ValueSource by deserializing a Shape in from BinaryDocValues using BinaryCodec.

View File

@ -30,10 +30,8 @@ import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
@ -124,17 +122,6 @@ public class PointVectorStrategy extends SpatialStrategy {
return new DistanceValueSource(this, queryPoint, multiplier);
}
@Override
public Filter makeFilter(SpatialArgs args) {
//unwrap the CSQ from makeQuery
ConstantScoreQuery csq = makeQuery(args);
Query sub = csq.getQuery();
if (sub instanceof Filter)
return (Filter) sub;
else
return new QueryWrapperFilter(sub);
}
@Override
public ConstantScoreQuery makeQuery(SpatialArgs args) {
if(! SpatialOperation.is( args.getOperation(),

View File

@ -17,6 +17,11 @@ package org.apache.lucene.spatial;
* limitations under the License.
*/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import com.spatial4j.core.context.SpatialContext;
@ -34,11 +39,6 @@ import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.spatial.vector.PointVectorStrategy;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Based off of Solr 3's SpatialFilterTest.
*/
@ -165,12 +165,7 @@ public class PortedSolr3Test extends StrategyTestCase {
SpatialArgs args = new SpatialArgs(op,shape);
//args.setDistPrecision(0.025);
Query query;
if (random().nextBoolean()) {
query = strategy.makeQuery(args);
} else {
query = strategy.makeFilter(args);
}
Query query = strategy.makeQuery(args);
SearchResults results = executeQuery(query, 100);
assertEquals(""+shape,assertNumFound,results.numFound);
if (assertIds != null) {

View File

@ -77,12 +77,6 @@ public class QueryEqualsHashCodeTest extends LuceneTestCase {
return strategy.makeQuery(args);
}
});
testEqualsHashcode(args1, args2, new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return strategy.makeFilter(args);
}
});
testEqualsHashcode(args1, args2, new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {

View File

@ -17,11 +17,12 @@ package org.apache.lucene.spatial;
* limitations under the License.
*/
import java.io.IOException;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceUtils;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericDocValuesField;
@ -32,9 +33,9 @@ import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.StoredDocument;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
@ -48,8 +49,6 @@ import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.LuceneTestCase;
import java.io.IOException;
/**
* This class serves as example code to show how to use the Lucene spatial
* module.
@ -151,8 +150,8 @@ public class SpatialExample extends LuceneTestCase {
//note: SpatialArgs can be parsed from a string
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects,
ctx.makeCircle(-80.0, 33.0, DistanceUtils.dist2Degrees(200, DistanceUtils.EARTH_MEAN_RADIUS_KM)));
Filter filter = strategy.makeFilter(args);
TopDocs docs = indexSearcher.search(filter, 10, idSort);
Query query = strategy.makeQuery(args);
TopDocs docs = indexSearcher.search(query, 10, idSort);
assertDocMatchedIds(indexSearcher, docs, 2);
//Now, lets get the distance for the 1st doc via computing from stored point value:
// (this computation is usually not redundant)

View File

@ -31,12 +31,12 @@ import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape;
import com.spatial4j.core.shape.SpatialRelation;
import com.spatial4j.core.shape.impl.RectangleImpl;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TotalHitCountCollector;
import org.apache.lucene.spatial.StrategyTestCase;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.util.Bits;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -185,7 +185,7 @@ public class HeatmapFacetCounterTest extends StrategyTestCase {
// coincides with a grid line due due to edge overlap issue for some grid implementations (geo & quad).
return false;
}
Filter filter = null; //FYI testing filtering of underlying PrefixTreeFacetCounter is done in another test
Bits filter = null; //FYI testing filtering of underlying PrefixTreeFacetCounter is done in another test
//Calculate facets
final int maxCells = 10_000;
final HeatmapFacetCounter.Heatmap heatmap = HeatmapFacetCounter.calcFacets(
@ -232,7 +232,7 @@ public class HeatmapFacetCounterTest extends StrategyTestCase {
private int countMatchingDocsAtLevel(Point pt, int facetLevel) throws IOException {
// we use IntersectsPrefixTreeFilter directly so that we can specify the level to go to exactly.
RecursivePrefixTreeStrategy strategy = (RecursivePrefixTreeStrategy) this.strategy;
Filter filter = new IntersectsPrefixTreeFilter(
Query filter = new IntersectsPrefixTreeQuery(
pt, strategy.getFieldName(), grid, facetLevel, grid.getMaxLevels());
final TotalHitCountCollector collector = new TotalHitCountCollector();
indexSearcher.search(filter, collector);

View File

@ -25,10 +25,11 @@ import java.util.List;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.Term;
import org.apache.lucene.queries.TermsQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SimpleCollector;
import org.apache.lucene.spatial.StrategyTestCase;
import org.apache.lucene.spatial.prefix.NumberRangePrefixTreeStrategy.Facets;
import org.apache.lucene.spatial.prefix.tree.Cell;
@ -36,6 +37,8 @@ import org.apache.lucene.spatial.prefix.tree.CellIterator;
import org.apache.lucene.spatial.prefix.tree.DateRangePrefixTree;
import org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree;
import org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree.UnitNRShape;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
import org.junit.Before;
import org.junit.Test;
@ -110,8 +113,8 @@ public class NumberRangeFacetsTest extends StrategyTestCase {
detailLevel = -1 * detailLevel;
}
//Randomly pick a filter
Filter filter = null;
//Randomly pick a filter/acceptDocs
Bits topAcceptDocs = null;
List<Integer> acceptFieldIds = new ArrayList<>();
if (usually()) {
//get all possible IDs into a list, random shuffle it, then randomly choose how many of the first we use to
@ -129,21 +132,22 @@ public class NumberRangeFacetsTest extends StrategyTestCase {
for (Integer acceptDocId : acceptFieldIds) {
terms.add(new Term("id", acceptDocId.toString()));
}
filter = new QueryWrapperFilter(new TermsQuery(terms));
topAcceptDocs = searchForDocBits(new TermsQuery(terms));
}
}
//Lets do it!
NumberRangePrefixTree.NRShape facetRange = tree.toRangeShape(tree.toShape(leftCal), tree.toShape(rightCal));
Facets facets = ((NumberRangePrefixTreeStrategy) strategy)
.calcFacets(indexSearcher.getTopReaderContext(), filter, facetRange, detailLevel);
.calcFacets(indexSearcher.getTopReaderContext(), topAcceptDocs, facetRange, detailLevel);
//System.out.println("Q: " + queryIdx + " " + facets);
//Verify results. We do it by looping over indexed shapes and reducing the facet counts.
Shape facetShapeRounded = facetRange.roundToLevel(detailLevel);
for (int indexedShapeId = 0; indexedShapeId < indexedShapes.size(); indexedShapeId++) {
if (filter != null && !acceptFieldIds.contains(indexedShapeId)) {
if (topAcceptDocs != null && !acceptFieldIds.contains(indexedShapeId)) {
continue;// this doc was filtered out via acceptDocs
}
Shape indexedShape = indexedShapes.get(indexedShapeId);
@ -207,6 +211,29 @@ public class NumberRangeFacetsTest extends StrategyTestCase {
}
}
private Bits searchForDocBits(Query query) throws IOException {
FixedBitSet bitSet = new FixedBitSet(indexSearcher.getIndexReader().maxDoc());
indexSearcher.search(query,
new SimpleCollector() {
int leafDocBase;
@Override
public void collect(int doc) throws IOException {
bitSet.set(leafDocBase + doc);
}
@Override
protected void doSetNextReader(LeafReaderContext context) throws IOException {
leafDocBase = context.docBase;
}
@Override
public boolean needsScores() {
return false;
}
});
return bitSet;
}
private void preQueryHavoc() {
if (strategy instanceof RecursivePrefixTreeStrategy) {
RecursivePrefixTreeStrategy rpts = (RecursivePrefixTreeStrategy) strategy;

View File

@ -153,7 +153,7 @@ public class RandomSpatialOpFuzzyPrefixTreeTest extends StrategyTestCase {
doTest(SpatialOperation.Contains);
}
/** See LUCENE-5062, {@link ContainsPrefixTreeFilter#multiOverlappingIndexedShapes}. */
/** See LUCENE-5062, {@link ContainsPrefixTreeQuery#multiOverlappingIndexedShapes}. */
@Test
public void testContainsPairOverlap() throws IOException {
setupQuadGrid(3, randomBoolean());

View File

@ -17,16 +17,14 @@ package org.apache.lucene.spatial.serialized;
* limitations under the License.
*/
import java.io.IOException;
import com.spatial4j.core.context.SpatialContext;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.SpatialMatchConcern;
import org.apache.lucene.spatial.SpatialTestQuery;
import org.apache.lucene.spatial.StrategyTestCase;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
public class SerializedStrategyTest extends StrategyTestCase {
@Before
@ -42,12 +40,6 @@ public class SerializedStrategyTest extends StrategyTestCase {
return (strategy instanceof SerializedDVStrategy);
}
//called by StrategyTestCase; we can't let it call our makeQuery which will UOE ex.
@Override
protected Query makeQuery(SpatialTestQuery q) {
return strategy.makeFilter(q.args);
}
@Test
public void testBasicOperaions() throws IOException {
getAddAndVerifyIndexedDocuments(DATA_SIMPLE_BBOX);

View File

@ -39,6 +39,7 @@ import org.apache.lucene.spatial.prefix.HeatmapFacetCounter;
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.util.Bits;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.FacetParams;
@ -50,6 +51,7 @@ import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.RptWithGeometrySpatialField;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.SpatialRecursivePrefixTreeFieldType;
import org.apache.solr.search.BitDocSet;
import org.apache.solr.search.DocSet;
import org.apache.solr.search.QueryParsing;
import org.apache.solr.util.DistanceUnits;
@ -133,13 +135,32 @@ public class SpatialHeatmapFacets {
gridLevel = strategy.getGrid().getLevelForDistance(distErr);
}
// Turn docSet into Bits
Bits topAcceptDocs;
if (docSet instanceof BitDocSet) {
BitDocSet set = (BitDocSet) docSet;
topAcceptDocs = set.getBits();
} else {
topAcceptDocs = new Bits() {
@Override
public boolean get(int index) {
return docSet.exists(index);
}
@Override
public int length() {
return rb.req.getSearcher().maxDoc();
}
};
}
//Compute!
final HeatmapFacetCounter.Heatmap heatmap;
try {
heatmap = HeatmapFacetCounter.calcFacets(
strategy,
rb.req.getSearcher().getTopReaderContext(),
docSet.getTopFilter(),
topAcceptDocs,
boundsShape,
gridLevel,
params.getFieldInt(fieldKey, FacetParams.FACET_HEATMAP_MAX_CELLS, 100_000) // will throw if exceeded

View File

@ -38,7 +38,6 @@ import com.spatial4j.core.distance.DistanceUtils;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.StorableField;
@ -46,7 +45,6 @@ import org.apache.lucene.queries.function.FunctionQuery;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SortField;
import org.apache.lucene.spatial.SpatialStrategy;
@ -333,17 +331,13 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
T strategy = getStrategy(field.getName());
SolrParams localParams = parser.getLocalParams();
//See SOLR-2883 needScore
String scoreParam = (localParams == null ? null : localParams.get(SCORE_PARAM));
//We get the valueSource for the score then the filter and combine them.
ValueSource valueSource = getValueSourceFromSpatialArgs(parser, field, spatialArgs, scoreParam, strategy);
if (valueSource == null) {
//FYI Solr FieldType doesn't have a getFilter(). We'll always grab
// getQuery() but it's possible a strategy has a more efficient getFilter
// that could be wrapped -- no way to know.
//See SOLR-2883 needScore
return strategy.makeQuery(spatialArgs); //ConstantScoreQuery
return strategy.makeQuery(spatialArgs); //assumed constant scoring
}
FunctionQuery functionQuery = new FunctionQuery(valueSource);
@ -351,10 +345,10 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
if (localParams != null && !localParams.getBool(FILTER_PARAM, true))
return functionQuery;
Filter filter = strategy.makeFilter(spatialArgs);
Query filterQuery = strategy.makeQuery(spatialArgs);
return new BooleanQuery.Builder()
.add(functionQuery, Occur.MUST)
.add(filter, Occur.FILTER)
.add(functionQuery, Occur.MUST)//matches everything and provides score
.add(filterQuery, Occur.FILTER)//filters (score isn't used)
.build();
}