Index: lucene/spatial/src/test/org/apache/lucene/spatial/vector/TestTwoDoublesStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/vector/TestTwoDoublesStrategy.java (revision 1384918) +++ lucene/spatial/src/test/org/apache/lucene/spatial/vector/TestTwoDoublesStrategy.java (revision ) @@ -1,0 +1,0 @@ Index: lucene/spatial/src/test/org/apache/lucene/spatial/PortedSolr3Test.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/PortedSolr3Test.java (revision 1384918) +++ lucene/spatial/src/test/org/apache/lucene/spatial/PortedSolr3Test.java (revision ) @@ -24,10 +24,6 @@ import com.spatial4j.core.io.ShapeReadWriter; 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.StoredField; -import org.apache.lucene.document.StringField; import org.apache.lucene.search.FilteredQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; @@ -38,6 +34,7 @@ import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; import org.apache.lucene.spatial.query.SpatialArgs; import org.apache.lucene.spatial.query.SpatialOperation; +import org.apache.lucene.spatial.vector.TwoDoublesStrategy; import org.junit.Test; import java.io.IOException; @@ -71,6 +68,9 @@ strategy = new TermQueryPrefixTreeStrategy(grid, "termquery_geohash"); ctorArgs.add(new Object[]{new Param(strategy, "termquery_geohash")}); + strategy = new TwoDoublesStrategy(ctx, "twodoubles"); + ctorArgs.add(new Object[]{new Param(strategy, "twodoubles")}); + return ctorArgs; } @@ -157,51 +157,7 @@ checkHitsBBox("43.517030,-96.789603", 110, 1, 17); } - /** - * This test is similar to a Solr 3 spatial test. - */ - @Test - public void testDistanceOrder() throws IOException { - adoc("100","1,2"); - adoc("101","4,-1"); - commit(); - double km1000inDeg = DistanceUtils.dist2Degrees(1000, DistanceUtils.EARTH_MEAN_RADIUS_KM); - - //query closer to #100 - checkHitsOrdered("Intersects(Circle(3,4 d="+km1000inDeg+"))", "101", "100"); - //query closer to #101 - checkHitsOrdered("Intersects(Circle(4,0 d="+km1000inDeg+"))", "100", "101"); - } - - private void checkHitsOrdered(String spatialQ, String... ids) { - SpatialArgs args = this.argsParser.parse(spatialQ,ctx); - Query query = strategy.makeQuery(args); - SearchResults results = executeQuery(query, 100); - String[] resultIds = new String[results.numFound]; - int i = 0; - for (SearchResult result : results.results) { - resultIds[i++] = result.document.get("id"); - } - assertArrayEquals("order matters",ids, resultIds); - } - //---- these are similar to Solr test methods - - private void adoc(String idStr, String shapeStr) throws IOException { - Shape shape = new ShapeReadWriter(ctx).readShape(shapeStr); - addDocument(newDoc(idStr,shape)); - } - - private Document newDoc(String id, Shape shape) { - Document doc = new Document(); - doc.add(new StringField("id", id, Field.Store.YES)); - for (Field f : strategy.createIndexableFields(shape)) { - doc.add(f); - } - if (storeShape) - doc.add(new StoredField(strategy.getFieldName(), ctx.toString(shape))); - return doc; - } private void checkHitsCircle(String ptStr, double distKM, int assertNumFound, int... assertIds) { _checkHits(false, ptStr, distKM, assertNumFound, assertIds); Index: lucene/spatial/src/java/org/apache/lucene/spatial/util/ValueSourceFilter.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/util/ValueSourceFilter.java (revision 1384918) +++ lucene/spatial/src/java/org/apache/lucene/spatial/util/ValueSourceFilter.java (revision ) @@ -29,7 +29,7 @@ /** * Filter that matches all documents where a valuesource is - * in between a range of min and max + * in between a range of min and max inclusive. * @lucene.internal */ public class ValueSourceFilter extends Filter { @@ -57,7 +57,7 @@ @Override public boolean match(int doc) { double val = values.doubleVal( doc ); - return val > min && val < max; + return val >= min && val <= max; } }; } 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 1384918) +++ lucene/spatial/src/java/org/apache/lucene/spatial/vector/TwoDoublesStrategy.java (revision ) @@ -30,6 +30,7 @@ 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.FilteredQuery; import org.apache.lucene.search.MatchAllDocsQuery; @@ -94,32 +95,47 @@ } @Override - public ValueSource makeValueSource(SpatialArgs args) { - Point p = args.getShape().getCenter(); - return new DistanceValueSource(this, p, ctx.getDistCalc()); + public ValueSource makeDistanceValueSource(Point queryPoint) { + return new DistanceValueSource(this, queryPoint); } @Override public Filter makeFilter(SpatialArgs args) { - if( args.getShape() instanceof Circle) { - if( SpatialOperation.is( args.getOperation(), - SpatialOperation.Intersects, - SpatialOperation.IsWithin )) { - Circle circle = (Circle)args.getShape(); - Query bbox = makeWithin(circle.getBoundingBox()); + //unwrap the CSQ from makeQuery + ConstantScoreQuery csq = makeQuery(args); + Filter filter = csq.getFilter(); + if (filter != null) + return filter; + else + return new QueryWrapperFilter(csq.getQuery()); + } - // Make the ValueSource - ValueSource valueSource = makeValueSource(args); - - return new ValueSourceFilter( - new QueryWrapperFilter( bbox ), valueSource, 0, circle.getRadius() ); + @Override + public ConstantScoreQuery makeQuery(SpatialArgs args) { + if(! SpatialOperation.is( args.getOperation(), + SpatialOperation.Intersects, + SpatialOperation.IsWithin )) + throw new UnsupportedSpatialOperation(args.getOperation()); + Shape shape = args.getShape(); + if (shape instanceof Rectangle) { + Rectangle bbox = (Rectangle) shape; + return new ConstantScoreQuery(makeWithin(bbox)); + } else if (shape instanceof Circle) { + Circle circle = (Circle)shape; + Rectangle bbox = circle.getBoundingBox(); + ValueSourceFilter vsf = new ValueSourceFilter( + new QueryWrapperFilter(makeWithin(bbox)), + makeDistanceValueSource(circle.getCenter()), + 0, + circle.getRadius() ); + return new ConstantScoreQuery(vsf); + } else { + throw new InvalidShapeException("Only Rectangles and Circles are currently supported, " + + "found [" + shape.getClass() + "]");//TODO - } - } + } + } - return new QueryWrapperFilter( makeQuery(args) ); - } - @Override - public Query makeQuery(SpatialArgs args) { + public Query makeQueryDistanceScore(SpatialArgs args) { // For starters, just limit the bbox Shape shape = args.getShape(); if (!(shape instanceof Rectangle || shape instanceof Circle)) { @@ -151,7 +167,7 @@ Circle circle = (Circle)args.getShape(); // Make the ValueSource - valueSource = makeValueSource(args); + valueSource = makeDistanceValueSource(shape.getCenter()); ValueSourceFilter vsf = new ValueSourceFilter( new QueryWrapperFilter( spatial ), valueSource, 0, circle.getRadius() ); @@ -171,7 +187,7 @@ valueSource = new CachingDoubleValueSource(valueSource); } else { - valueSource = makeValueSource(args); + valueSource = makeDistanceValueSource(shape.getCenter()); } Query spatialRankingQuery = new FunctionQuery(valueSource); BooleanQuery bq = new BooleanQuery(); @@ -182,49 +198,39 @@ /** * 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); - 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()), BooleanClause.Occur.SHOULD ); + bq.add(rangeQuery(fieldNameX, bbox.getMinX(), null/*+180*/), BooleanClause.Occur.SHOULD ); + bq.setMinimumNumberShouldMatch(1);//must match at least one of the SHOULD + } 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);