Don't assume fixed earth diameter in the geo-distance bounding box optimization.

We switched to Lucene's SloppyMath way of computing an approximate value of
the eath diameter given a latitude in order to compute distances, yet the
bounding box optimization of the geo distance filter still assumed a constant
earth diameter, equal to the average.

Close #6008
This commit is contained in:
Adrien Grand 2014-05-06 13:31:03 +02:00
parent 6feeac98c8
commit c306d8c5f5
3 changed files with 50 additions and 2 deletions

View File

@ -140,7 +140,8 @@ public enum GeoDistance {
public static DistanceBoundingCheck distanceBoundingCheck(double sourceLatitude, double sourceLongitude, double distance, DistanceUnit unit) {
// angular distance in radians on a great circle
double radDist = distance / unit.getEarthRadius();
// assume worst-case: use the minor axis
double radDist = unit.toMeters(distance) / GeoUtils.EARTH_SEMI_MINOR_AXIS;
double radLat = Math.toRadians(sourceLatitude);
double radLon = Math.toRadians(sourceLongitude);

View File

@ -52,7 +52,7 @@ public class GeoDistanceFilter extends Filter {
private final IndexGeoPointFieldData indexFieldData;
private final GeoDistance.FixedSourceDistance fixedSourceDistance;
private GeoDistance.DistanceBoundingCheck distanceBoundingCheck;
private final GeoDistance.DistanceBoundingCheck distanceBoundingCheck;
private final Filter boundingBoxFilter;
public GeoDistanceFilter(double lat, double lon, double distance, GeoDistance geoDistance, IndexGeoPointFieldData indexFieldData, GeoPointFieldMapper mapper,
@ -64,6 +64,7 @@ public class GeoDistanceFilter extends Filter {
this.indexFieldData = indexFieldData;
this.fixedSourceDistance = geoDistance.fixedSourceDistance(lat, lon, DistanceUnit.DEFAULT);
GeoDistance.DistanceBoundingCheck distanceBoundingCheck = null;
if (optimizeBbox != null && !"none".equals(optimizeBbox)) {
distanceBoundingCheck = GeoDistance.distanceBoundingCheck(lat, lon, distance, DistanceUnit.DEFAULT);
if ("memory".equals(optimizeBbox)) {
@ -78,6 +79,7 @@ public class GeoDistanceFilter extends Filter {
distanceBoundingCheck = GeoDistance.ALWAYS_INSTANCE;
boundingBoxFilter = null;
}
this.distanceBoundingCheck = distanceBoundingCheck;
}
public double lat() {

View File

@ -19,7 +19,9 @@
package org.elasticsearch.search.geo;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoHashUtils;
import org.elasticsearch.common.unit.DistanceUnit;
@ -36,6 +38,9 @@ import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.FilterBuilders.*;
@ -633,4 +638,44 @@ public class GeoDistanceTests extends ElasticsearchIntegrationTest {
assertHitCount(result, 1);
}
private double randomLon() {
return randomDouble() * 360 - 180;
}
private double randomLat() {
return randomDouble() * 180 - 90;
}
public void testDuelOptimizations() throws Exception {
assertAcked(prepareCreate("index").addMapping("type", "location", "type=geo_point,lat_lon=true"));
final int numDocs = scaledRandomIntBetween(3000, 10000);
List<IndexRequestBuilder> docs = new ArrayList<>();
for (int i = 0; i < numDocs; ++i) {
docs.add(client().prepareIndex("index", "type").setSource(jsonBuilder().startObject().startObject("location").field("lat", randomLat()).field("lon", randomLon()).endObject().endObject()));
}
indexRandom(true, docs);
ensureSearchable();
for (int i = 0; i < 10; ++i) {
final double originLat = randomLat();
final double originLon = randomLon();
final String distance = DistanceUnit.KILOMETERS.toString(randomInt(10000));
for (GeoDistance geoDistance : Arrays.asList(GeoDistance.ARC, GeoDistance.SLOPPY_ARC)) {
logger.info("Now testing GeoDistance={}, distance={}, origin=({}, {})", geoDistance, distance, originLat, originLon);
long matches = -1;
for (String optimizeBbox : Arrays.asList("none", "memory", "indexed")) {
SearchResponse resp = client().prepareSearch("index").setSearchType(SearchType.COUNT).setQuery(QueryBuilders.constantScoreQuery(
FilterBuilders.geoDistanceFilter("location").point(originLat, originLon).distance(distance).geoDistance(geoDistance).optimizeBbox(optimizeBbox))).execute().actionGet();
assertSearchResponse(resp);
logger.info("{} -> {} hits", optimizeBbox, resp.getHits().totalHits());
if (matches < 0) {
matches = resp.getHits().totalHits();
} else {
assertEquals(matches, resp.getHits().totalHits());
}
}
}
}
}
}