mirror of https://github.com/apache/lucene.git
LUCENE-7215: don't invoke full haversin for LatLonPoint.newDistanceQuery
This commit is contained in:
parent
1a1c8dbfb3
commit
2335a458d8
|
@ -39,7 +39,8 @@ Optimizations
|
||||||
* LUCENE-7071: Reduce bytes copying in OfflineSorter, giving ~10%
|
* LUCENE-7071: Reduce bytes copying in OfflineSorter, giving ~10%
|
||||||
speedup on merging 2D LatLonPoint values (Mike McCandless)
|
speedup on merging 2D LatLonPoint values (Mike McCandless)
|
||||||
|
|
||||||
* LUCENE-7105: Optimize LatLonPoint's newDistanceQuery. (Robert Muir)
|
* LUCENE-7105, LUCENE-7215: Optimize LatLonPoint's newDistanceQuery.
|
||||||
|
(Robert Muir)
|
||||||
|
|
||||||
* LUCENE-7109: LatLonPoint's newPolygonQuery supports two-phase
|
* LUCENE-7109: LatLonPoint's newPolygonQuery supports two-phase
|
||||||
iteration. (Robert Muir)
|
iteration. (Robert Muir)
|
||||||
|
|
|
@ -150,6 +150,10 @@ public class TestSloppyMath extends LuceneTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testHaversinFromSortKey() {
|
||||||
|
assertEquals(0.0, haversinMeters(0), 0.0D);
|
||||||
|
}
|
||||||
|
|
||||||
public void testAgainstSlowVersion() {
|
public void testAgainstSlowVersion() {
|
||||||
for (int i = 0; i < 100_000; i++) {
|
for (int i = 0; i < 100_000; i++) {
|
||||||
double lat1 = GeoTestUtil.nextLatitude();
|
double lat1 = GeoTestUtil.nextLatitude();
|
||||||
|
|
|
@ -97,15 +97,8 @@ final class LatLonPointDistanceQuery extends Query {
|
||||||
NumericUtils.intToSortableBytes(Integer.MAX_VALUE, minLon2, 0);
|
NumericUtils.intToSortableBytes(Integer.MAX_VALUE, minLon2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute a maximum partial haversin: unless our box is crazy, we can use this bound
|
// compute exact sort key: avoid any asin() computations
|
||||||
// to reject edge cases faster in visit()
|
final double sortKey = sortKey(radiusMeters);
|
||||||
final double maxPartialDistance;
|
|
||||||
if (box.maxLon - longitude < 90 && longitude - box.minLon < 90) {
|
|
||||||
maxPartialDistance = Math.max(SloppyMath.haversinSortKey(latitude, longitude, latitude, box.maxLon),
|
|
||||||
SloppyMath.haversinSortKey(latitude, longitude, box.maxLat, longitude));
|
|
||||||
} else {
|
|
||||||
maxPartialDistance = Double.POSITIVE_INFINITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
final double axisLat = Rectangle.axisLat(latitude, radiusMeters);
|
final double axisLat = Rectangle.axisLat(latitude, radiusMeters);
|
||||||
|
|
||||||
|
@ -160,13 +153,9 @@ final class LatLonPointDistanceQuery extends Query {
|
||||||
double docLatitude = decodeLatitude(packedValue, 0);
|
double docLatitude = decodeLatitude(packedValue, 0);
|
||||||
double docLongitude = decodeLongitude(packedValue, Integer.BYTES);
|
double docLongitude = decodeLongitude(packedValue, Integer.BYTES);
|
||||||
|
|
||||||
// first check the partial distance, if its more than that, it can't be <= radiusMeters
|
// its a match only if its sortKey <= our sortKey
|
||||||
double h1 = SloppyMath.haversinSortKey(latitude, longitude, docLatitude, docLongitude);
|
if (SloppyMath.haversinSortKey(latitude, longitude, docLatitude, docLongitude) <= sortKey) {
|
||||||
if (h1 <= maxPartialDistance) {
|
result.add(docID);
|
||||||
// fully confirm with part 2:
|
|
||||||
if (SloppyMath.haversinMeters(h1) <= radiusMeters) {
|
|
||||||
result.add(docID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,20 +186,20 @@ final class LatLonPointDistanceQuery extends Query {
|
||||||
|
|
||||||
if ((longitude < lonMin || longitude > lonMax) && (axisLat+ Rectangle.AXISLAT_ERROR < latMin || axisLat- Rectangle.AXISLAT_ERROR > latMax)) {
|
if ((longitude < lonMin || longitude > lonMax) && (axisLat+ Rectangle.AXISLAT_ERROR < latMin || axisLat- Rectangle.AXISLAT_ERROR > latMax)) {
|
||||||
// circle not fully inside / crossing axis
|
// circle not fully inside / crossing axis
|
||||||
if (SloppyMath.haversinMeters(latitude, longitude, latMin, lonMin) > radiusMeters &&
|
if (SloppyMath.haversinSortKey(latitude, longitude, latMin, lonMin) > sortKey &&
|
||||||
SloppyMath.haversinMeters(latitude, longitude, latMin, lonMax) > radiusMeters &&
|
SloppyMath.haversinSortKey(latitude, longitude, latMin, lonMax) > sortKey &&
|
||||||
SloppyMath.haversinMeters(latitude, longitude, latMax, lonMin) > radiusMeters &&
|
SloppyMath.haversinSortKey(latitude, longitude, latMax, lonMin) > sortKey &&
|
||||||
SloppyMath.haversinMeters(latitude, longitude, latMax, lonMax) > radiusMeters) {
|
SloppyMath.haversinSortKey(latitude, longitude, latMax, lonMax) > sortKey) {
|
||||||
// no points inside
|
// no points inside
|
||||||
return Relation.CELL_OUTSIDE_QUERY;
|
return Relation.CELL_OUTSIDE_QUERY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lonMax - longitude < 90 && longitude - lonMin < 90 &&
|
if (lonMax - longitude < 90 && longitude - lonMin < 90 &&
|
||||||
SloppyMath.haversinMeters(latitude, longitude, latMin, lonMin) <= radiusMeters &&
|
SloppyMath.haversinSortKey(latitude, longitude, latMin, lonMin) <= sortKey &&
|
||||||
SloppyMath.haversinMeters(latitude, longitude, latMin, lonMax) <= radiusMeters &&
|
SloppyMath.haversinSortKey(latitude, longitude, latMin, lonMax) <= sortKey &&
|
||||||
SloppyMath.haversinMeters(latitude, longitude, latMax, lonMin) <= radiusMeters &&
|
SloppyMath.haversinSortKey(latitude, longitude, latMax, lonMin) <= sortKey &&
|
||||||
SloppyMath.haversinMeters(latitude, longitude, latMax, lonMax) <= radiusMeters) {
|
SloppyMath.haversinSortKey(latitude, longitude, latMax, lonMax) <= sortKey) {
|
||||||
// we are fully enclosed, collect everything within this subtree
|
// we are fully enclosed, collect everything within this subtree
|
||||||
return Relation.CELL_INSIDE_QUERY;
|
return Relation.CELL_INSIDE_QUERY;
|
||||||
} else {
|
} else {
|
||||||
|
@ -230,6 +219,39 @@ final class LatLonPointDistanceQuery extends Query {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binary search to find the exact sortKey needed to match the specified radius
|
||||||
|
* any sort key <= this is a query match.
|
||||||
|
*/
|
||||||
|
static double sortKey(double radius) {
|
||||||
|
// effectively infinite
|
||||||
|
if (radius >= SloppyMath.haversinMeters(Double.MAX_VALUE)) {
|
||||||
|
return SloppyMath.haversinMeters(Double.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a search through non-negative long space only
|
||||||
|
long lo = 0;
|
||||||
|
long hi = Double.doubleToRawLongBits(Double.MAX_VALUE);
|
||||||
|
while (lo <= hi) {
|
||||||
|
long mid = (lo + hi) >>> 1;
|
||||||
|
double sortKey = Double.longBitsToDouble(mid);
|
||||||
|
double midRadius = SloppyMath.haversinMeters(sortKey);
|
||||||
|
if (midRadius == radius) {
|
||||||
|
return sortKey;
|
||||||
|
} else if (midRadius > radius) {
|
||||||
|
hi = mid - 1;
|
||||||
|
} else {
|
||||||
|
lo = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found: this is because a user can supply an arbitrary radius, one that we will never
|
||||||
|
// calculate exactly via our haversin method.
|
||||||
|
double ceil = Double.longBitsToDouble(lo);
|
||||||
|
assert SloppyMath.haversinMeters(ceil) > radius;
|
||||||
|
return ceil;
|
||||||
|
}
|
||||||
|
|
||||||
public String getField() {
|
public String getField() {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue