mirror of https://github.com/apache/lucene.git
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:
parent
333cf6ea2e
commit
c0bb4b8b35
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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;
|
|
@ -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) {
|
||||
|
|
|
@ -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 + "," +
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue