SOLR-4230 geofilt and bbox support for Solr 4 spatial

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1428063 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David Wayne Smiley 2013-01-02 21:27:03 +00:00
parent 463d10dc4a
commit 35caf2c6bd
2 changed files with 65 additions and 21 deletions

View File

@ -22,7 +22,9 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.context.SpatialContextFactory;
import com.spatial4j.core.distance.DistanceUtils;
import com.spatial4j.core.exception.InvalidShapeException;
import com.spatial4j.core.io.ParseUtils;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Rectangle;
import com.spatial4j.core.shape.Shape;
@ -43,6 +45,7 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.response.TextResponseWriter;
import org.apache.solr.search.QParser;
import org.apache.solr.search.SpatialOptions;
import org.apache.solr.util.MapListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -57,7 +60,7 @@ import java.util.concurrent.ExecutionException;
*
* @lucene.experimental
*/
public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extends FieldType {
public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extends FieldType implements SpatialQueryable {
/** A local-param with one of "none" (default), "distance", or "recipDistance". */
public static final String SCORE_PARAM = "score";
@ -160,6 +163,35 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
// Query Support
//--------------------------------------------------------------
/**
* Implemented for compatibility with Solr 3 spatial geofilt & bbox query parsers:
* {@link SpatialQueryable}.
*/
@Override
public Query createSpatialQuery(QParser parser, SpatialOptions options) {
//--WARNING: the code from here to the next marker is identical to LatLonType's impl.
double[] point = null;
try {
point = ParseUtils.parseLatitudeLongitude(options.pointStr);
} catch (InvalidShapeException e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
}
// lat & lon in degrees
double latCenter = point[0];
double lonCenter = point[1];
double distDeg = DistanceUtils.dist2Degrees(options.distance, options.radius);
//--END-WARNING
Shape shape = ctx.makeCircle(lonCenter, latCenter, distDeg);
if (options.bbox)
shape = shape.getBoundingBox();
SpatialArgs spatialArgs = new SpatialArgs(SpatialOperation.Intersects, shape);
return getQueryFromSpatialArgs(parser, options.field, spatialArgs);
}
@Override
public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) {
if (!minInclusive || !maxInclusive)

View File

@ -149,7 +149,7 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
assertQ(req(
"fl", "id," + fieldName, "q", "*:*", "rows", "1000",
"fq", "{!field needScore=false f="+fieldName+"}Intersects(Circle(89.9,-130 d=9))"),
"fq", "{!field f="+fieldName+"}Intersects(Circle(89.9,-130 d=9))"),
"//result/doc/*[@name='" + fieldName + "']//text()='" + OUT + "'");
}
@ -175,26 +175,37 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
// that there may be a more specific detailed id to investigate.
tests[i++] = "*[count(//doc)=" + count + "]";
//never actually need the score but lets test
String score = new String[]{null, "none","distance","recipDistance"}[random().nextInt(4)];
//Test using the Solr 4 syntax
{
//never actually need the score but lets test
String score = new String[]{null, "none","distance","recipDistance"}[random().nextInt(4)];
double distDEG = DistanceUtils.dist2Degrees(distKM, DistanceUtils.EARTH_MEAN_RADIUS_KM);
String circleStr = "Circle(" + ptStr.replaceAll(" ", "") + " d=" + distDEG + ")";
String shapeStr;
if (exact) {
shapeStr = circleStr;
} else {//bbox
//the GEO is an assumption
SpatialContext ctx = SpatialContext.GEO;
shapeStr = ctx.toString( ctx.readShape(circleStr).getBoundingBox() );
double distDEG = DistanceUtils.dist2Degrees(distKM, DistanceUtils.EARTH_MEAN_RADIUS_KM);
String circleStr = "Circle(" + ptStr.replaceAll(" ", "") + " d=" + distDEG + ")";
String shapeStr;
if (exact) {
shapeStr = circleStr;
} else {//bbox
//the GEO is an assumption
SpatialContext ctx = SpatialContext.GEO;
shapeStr = ctx.toString( ctx.readShape(circleStr).getBoundingBox() );
}
//FYI default distErrPct=0.025 works with the tests in this file
assertQ(req(
"fl", "id", "q","*:*", "rows", "1000",
"fq", "{!field f=" + fieldName + (score==null?"":" score="+score)
+ "}Intersects(" + shapeStr + ")"),
tests);
}
//Test using the Solr 3 syntax
{
assertQ(req(
"fl", "id", "q", "*:*", "rows", "1000",
"fq", "{!" + (exact ? "geofilt" : "bbox") + " sfield=" + fieldName + " pt='" + ptStr + "' d=" + distKM + "}"),
tests);
}
//FYI default distErrPct=0.025 works with the tests in this file
assertQ(req(
"fl", "id", "q","*:*", "rows", "1000",
"fq", "{!field f=" + fieldName + (score==null?"":" score="+score)
+ "}Intersects(" + shapeStr + ")"),
tests);
}
@Test
@ -203,10 +214,11 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
//match docId 1
int docId = 1;
int count = 1;
boolean needScore = random().nextBoolean();//never actually need the score but lets test
String score = random().nextBoolean() ? "none" : "distance";//never actually need the score but lets test
assertQ(req(
"fl", "id", "q","*:*", "rows", "1000",
"fq", "{! needScore="+needScore+" df="+fieldName+"}[32,-80 TO 33,-79]"),//lower-left to upper-right
"fq", "{! score="+score+" df="+fieldName+"}[32,-80 TO 33,-79]"),//lower-left to upper-right
"//result/doc/*[@name='id'][.='" + docId + "']",
"*[count(//doc)=" + count + "]");