Index: lucene/spatial/src/test/org/apache/lucene/spatial/SpatialExample.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/SpatialExample.java (revision 1383965) +++ lucene/spatial/src/test/org/apache/lucene/spatial/SpatialExample.java (revision ) @@ -45,7 +45,6 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.util.LuceneTestCase; -import org.apache.lucene.util.Version; import java.io.IOException; @@ -153,7 +152,7 @@ { SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects,//doesn't matter ctx.makePoint(60, -50)); - ValueSource valueSource = strategy.makeValueSource(args);//the distance (in degrees) + ValueSource valueSource = strategy.makeDistanceValueSource(args);//the distance (in degrees) Sort reverseDistSort = new Sort(valueSource.getSortField(false)).rewrite(indexSearcher);//true=asc dist TopDocs docs = indexSearcher.search(new MatchAllDocsQuery(), 10, reverseDistSort); assertDocMatchedIds(indexSearcher, docs, 4, 20, 2); Index: lucene/spatial/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java (revision 1383965) +++ lucene/spatial/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java (revision ) @@ -141,12 +141,12 @@ } @Override - public ValueSource makeValueSource(SpatialArgs args) { + public ValueSource makeDistanceValueSource(SpatialArgs args) { DistanceCalculator calc = grid.getSpatialContext().getDistCalc(); - return makeValueSource(args, calc); + return makeDistanceValueSource(args, calc); } - public ValueSource makeValueSource(SpatialArgs args, DistanceCalculator calc) { + public ValueSource makeDistanceValueSource(SpatialArgs args, DistanceCalculator calc) { PointPrefixTreeFieldCacheProvider p = provider.get( getFieldName() ); if( p == null ) { synchronized (this) {//double checked locking idiom is okay since provider is threadsafe Index: lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java (revision 1384026) +++ lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java (revision ) @@ -20,11 +20,10 @@ import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.shape.Shape; import org.apache.lucene.document.Field; -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.FilteredQuery; -import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryWrapperFilter; import org.apache.lucene.spatial.query.SpatialArgs; /** @@ -50,6 +49,11 @@ * values of shapes, which is immaterial to indexing & search. *

* Thread-safe. + *

+ * Implementation note: subclasses must override at least one of + * {@link #makeFilter(org.apache.lucene.spatial.query.SpatialArgs)} or + * {@link #makeQuery(org.apache.lucene.spatial.query.SpatialArgs)} since + * the default implementations of each call the other. * * @lucene.experimental */ @@ -99,25 +103,30 @@ public abstract Field[] createIndexableFields(Shape shape); /** - * The value source yields a number that is proportional to the distance between the query shape and indexed data. + * Make a ValueSource returning the closest distance between the center of the + * indexed shape and the center of the query shape. */ - public abstract ValueSource makeValueSource(SpatialArgs args); + public abstract ValueSource makeDistanceValueSource(SpatialArgs args); /** - * Make a query which has a score based on the distance from the data to the query shape. - * The default implementation constructs a {@link FilteredQuery} based on - * {@link #makeFilter(org.apache.lucene.spatial.query.SpatialArgs)} and - * {@link #makeValueSource(org.apache.lucene.spatial.query.SpatialArgs)}. + * Make a (ConstantScore) Query based principally on {@link org.apache.lucene.spatial.query.SpatialOperation} + * and {@link Shape} from the supplied {@code args}. + * The default implementation is + *
return new ConstantScoreQuery(makeFilter(args));
*/ - public Query makeQuery(SpatialArgs args) { - Filter filter = makeFilter(args); - ValueSource vs = makeValueSource(args); - return new FilteredQuery(new FunctionQuery(vs), filter); + public ConstantScoreQuery makeQuery(SpatialArgs args) { + return new ConstantScoreQuery(makeFilter(args)); } + /** - * Make a Filter + * Make a Filter based principally on {@link org.apache.lucene.spatial.query.SpatialOperation} + * and {@link Shape} from the supplied {@code args}. + * The default implementation is + *
return new QueryWrapperFilter(makeQuery(args).getQuery());
*/ - public abstract Filter makeFilter(SpatialArgs args); + public Filter makeFilter(SpatialArgs args) { + return new QueryWrapperFilter(makeQuery(args).getQuery()); + } @Override public String toString() { Index: lucene/spatial/src/java/org/apache/lucene/spatial/vector/TwoDoublesStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/vector/TwoDoublesStrategy.java (revision 1384026) +++ lucene/spatial/src/java/org/apache/lucene/spatial/vector/TwoDoublesStrategy.java (revision ) @@ -26,6 +26,7 @@ import org.apache.lucene.document.DoubleField; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; +import org.apache.lucene.queries.ChainedFilter; import org.apache.lucene.queries.function.FunctionQuery; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.search.BooleanClause; @@ -94,40 +95,38 @@ } @Override - public ValueSource makeValueSource(SpatialArgs args) { + public ValueSource makeDistanceValueSource(SpatialArgs args) { Point p = args.getShape().getCenter(); return new DistanceValueSource(this, p, ctx.getDistCalc()); } @Override public Filter makeFilter(SpatialArgs args) { - if( args.getShape() instanceof Circle) { - if( SpatialOperation.is( args.getOperation(), + if( ! SpatialOperation.is( args.getOperation(), - SpatialOperation.Intersects, + SpatialOperation.Intersects, - SpatialOperation.IsWithin )) { - Circle circle = (Circle)args.getShape(); - Query bbox = makeWithin(circle.getBoundingBox()); - - // Make the ValueSource - ValueSource valueSource = makeValueSource(args); - - return new ValueSourceFilter( - new QueryWrapperFilter( bbox ), valueSource, 0, circle.getRadius() ); + SpatialOperation.IsWithin )) + throw new UnsupportedSpatialOperation(args.getOperation()); + Shape shape = args.getShape(); + Filter bboxFilter = new QueryWrapperFilter(makeWithinBox(shape.getBoundingBox())); + if (shape instanceof Rectangle || shape instanceof Point) { + return bboxFilter; + } else { + Filter[] filters = new Filter[2]; + filters[0] = bboxFilter; + filters[1] = new FieldCachePointShapeFilter(this, args); + return new ChainedFilter(filters, ChainedFilter.AND); - } - } + } + } - return new QueryWrapperFilter( makeQuery(args) ); - } - @Override - public Query makeQuery(SpatialArgs args) { + public Query makeScoringQuery(SpatialArgs args) { // For starters, just limit the bbox Shape shape = args.getShape(); - if (!(shape instanceof Rectangle || shape instanceof Circle)) { - throw new InvalidShapeException("Only Rectangles and Circles are currently supported, " + + if (!(shape instanceof Rectangle)) { + throw new InvalidShapeException("Only Rectangles are currently supported, " + "found [" + shape.getClass() + "]");//TODO } - Rectangle bbox = shape.getBoundingBox(); + Rectangle bbox = ((Rectangle)shape); if (bbox.getCrossesDateLine()) { throw new UnsupportedOperationException( "Crossing dateline not yet supported" ); @@ -141,17 +140,17 @@ if( SpatialOperation.is( op, SpatialOperation.BBoxWithin, SpatialOperation.BBoxIntersects ) ) { - spatial = makeWithin(bbox); + spatial = makeWithinBox(bbox); } else if( SpatialOperation.is( op, SpatialOperation.Intersects, SpatialOperation.IsWithin ) ) { - spatial = makeWithin(bbox); + spatial = makeWithinBox(bbox); if( args.getShape() instanceof Circle) { Circle circle = (Circle)args.getShape(); // Make the ValueSource - valueSource = makeValueSource(args); + valueSource = makeDistanceValueSource(args); ValueSourceFilter vsf = new ValueSourceFilter( new QueryWrapperFilter( spatial ), valueSource, 0, circle.getRadius() ); @@ -171,7 +170,7 @@ valueSource = new CachingDoubleValueSource(valueSource); } else { - valueSource = makeValueSource(args); + valueSource = makeDistanceValueSource(args); } Query spatialRankingQuery = new FunctionQuery(valueSource); BooleanQuery bq = new BooleanQuery(); @@ -182,49 +181,38 @@ /** * Constructs a query to retrieve documents that fully contain the input envelope. - * @return the spatial query */ - private Query makeWithin(Rectangle bbox) { - Query qX = NumericRangeQuery.newDoubleRange( - fieldNameX, - precisionStep, - bbox.getMinX(), - bbox.getMaxX(), - true, - true); - Query qY = NumericRangeQuery.newDoubleRange( - fieldNameY, - precisionStep, - bbox.getMinY(), - bbox.getMaxY(), - true, - true); - + private Query makeWithinBox(Rectangle bbox) { BooleanQuery bq = new BooleanQuery(); - bq.add(qX,BooleanClause.Occur.MUST); - bq.add(qY,BooleanClause.Occur.MUST); + BooleanClause.Occur MUST = BooleanClause.Occur.MUST; + if (bbox.getCrossesDateLine()) { + //use null as performance trick since no data will be beyond the world bounds + bq.add(rangeQuery(fieldNameX, null/*-180*/, bbox.getMaxX()), MUST); + bq.add(rangeQuery(fieldNameX, bbox.getMinX(), null/*+180*/), MUST); + } else { + bq.add(rangeQuery(fieldNameX, bbox.getMinX(), bbox.getMaxX()), MUST); + } + bq.add(rangeQuery(fieldNameY, bbox.getMinY(), bbox.getMaxY()), MUST); return bq; } + private NumericRangeQuery rangeQuery(String fieldName, Double min, Double max) { + return NumericRangeQuery.newDoubleRange( + fieldName, + precisionStep, + min, + max, + true, + true);//inclusive + } + /** * Constructs a query to retrieve documents that fully contain the input envelope. * @return the spatial query */ Query makeDisjoint(Rectangle bbox) { - Query qX = NumericRangeQuery.newDoubleRange( - fieldNameX, - precisionStep, - bbox.getMinX(), - bbox.getMaxX(), - true, - true); - Query qY = NumericRangeQuery.newDoubleRange( - fieldNameY, - precisionStep, - bbox.getMinY(), - bbox.getMaxY(), - true, - true); + Query qX = rangeQuery(fieldNameX, bbox.getMinX(), bbox.getMaxX()); + Query qY = rangeQuery(fieldNameY, bbox.getMinY(), bbox.getMaxY()); BooleanQuery bq = new BooleanQuery(); bq.add(qX,BooleanClause.Occur.MUST_NOT); Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java (revision 1384026) +++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java (revision ) @@ -114,7 +114,7 @@ //--------------------------------- @Override - public ValueSource makeValueSource(SpatialArgs args) { + public ValueSource makeDistanceValueSource(SpatialArgs args) { Shape shape = args.getShape(); if (!(shape instanceof Rectangle)) throw new IllegalArgumentException("Can only get valueSource by Rectangle, not " + shape); @@ -129,14 +129,13 @@ return new QueryWrapperFilter( spatial ); } - @Override - public Query makeQuery(SpatialArgs args) { + public Query makeQueryWithValueSource(SpatialArgs args, ValueSource valueSource) { BooleanQuery bq = new BooleanQuery(); Query spatial = makeSpatialQuery(args); bq.add(new ConstantScoreQuery(spatial), BooleanClause.Occur.MUST); // This part does the scoring - Query spatialRankingQuery = new FunctionQuery(makeValueSource(args)); + Query spatialRankingQuery = new FunctionQuery(valueSource); bq.add(spatialRankingQuery, BooleanClause.Occur.MUST); return bq; } Index: lucene/spatial/src/java/org/apache/lucene/spatial/vector/FieldCachePointShapeFilter.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/vector/FieldCachePointShapeFilter.java (revision ) +++ lucene/spatial/src/java/org/apache/lucene/spatial/vector/FieldCachePointShapeFilter.java (revision ) @@ -0,0 +1,49 @@ +package org.apache.lucene.spatial.vector; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.spatial4j.core.shape.Shape; +import org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.search.DocIdSet; +import org.apache.lucene.search.Filter; +import org.apache.lucene.spatial.query.SpatialArgs; +import org.apache.lucene.spatial.query.SpatialOperation; +import org.apache.lucene.util.Bits; + +import java.io.IOException; + +/** + * Matches indexed points against a query shape. + */ +public class FieldCachePointShapeFilter extends Filter { + private Shape queryShape; + private SpatialOperation op; + private TwoDoublesStrategy strategy; + + public FieldCachePointShapeFilter(TwoDoublesStrategy twoDoublesStrategy, SpatialArgs args) { + this.strategy = twoDoublesStrategy; + this.op = args.getOperation(); + this.queryShape = args.getShape(); + } + + @Override + public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException { + //TODO + } + +}