mirror of https://github.com/apache/lucene.git
LUCENE-7343: Cleanup GeoPoint by sharing relation logic and removing stale code.
This commit is contained in:
parent
aaddefe80e
commit
fc68bd90e6
|
@ -2,6 +2,7 @@ package org.apache.lucene.geo;
|
||||||
|
|
||||||
import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
|
import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
|
||||||
import static org.apache.lucene.util.SloppyMath.cos;
|
import static org.apache.lucene.util.SloppyMath.cos;
|
||||||
|
import static org.apache.lucene.util.SloppyMath.haversinMeters;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
@ -91,4 +92,37 @@ public final class GeoUtils {
|
||||||
public static double sloppySin(double a) {
|
public static double sloppySin(double a) {
|
||||||
return cos(a - PIO2);
|
return cos(a - PIO2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binary search to find the exact sortKey needed to match the specified radius
|
||||||
|
* any sort key lte this is a query match.
|
||||||
|
*/
|
||||||
|
public static double distanceQuerySortKey(double radius) {
|
||||||
|
// effectively infinite
|
||||||
|
if (radius >= haversinMeters(Double.MAX_VALUE)) {
|
||||||
|
return 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 = 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 haversinMeters(ceil) > radius;
|
||||||
|
return ceil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ final class LatLonPointDistanceQuery extends Query {
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute exact sort key: avoid any asin() computations
|
// compute exact sort key: avoid any asin() computations
|
||||||
final double sortKey = sortKey(radiusMeters);
|
final double sortKey = GeoUtils.distanceQuerySortKey(radiusMeters);
|
||||||
|
|
||||||
final double axisLat = Rectangle.axisLat(latitude, radiusMeters);
|
final double axisLat = Rectangle.axisLat(latitude, radiusMeters);
|
||||||
|
|
||||||
|
@ -215,39 +215,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
||||||
protected final double centerLon;
|
protected final double centerLon;
|
||||||
/** distance (in meters) from lat, lon center location */
|
/** distance (in meters) from lat, lon center location */
|
||||||
protected final double radiusMeters;
|
protected final double radiusMeters;
|
||||||
|
/** partial haversin computation */
|
||||||
|
protected final double sortKey;
|
||||||
|
|
||||||
// we must check these before passing to superclass or circleToBBox, or users can get a strange exception!
|
// we must check these before passing to superclass or circleToBBox, or users can get a strange exception!
|
||||||
private static double checkRadius(double radiusMeters) {
|
private static double checkRadius(double radiusMeters) {
|
||||||
|
@ -89,6 +91,7 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
||||||
this.centerLat = centerLat;
|
this.centerLat = centerLat;
|
||||||
this.centerLon = centerLon;
|
this.centerLon = centerLon;
|
||||||
this.radiusMeters = radiusMeters;
|
this.radiusMeters = radiusMeters;
|
||||||
|
this.sortKey = GeoUtils.distanceQuerySortKey(radiusMeters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.lucene.spatial.geopoint.search;
|
package org.apache.lucene.spatial.geopoint.search;
|
||||||
|
|
||||||
import org.apache.lucene.geo.Rectangle;
|
import org.apache.lucene.geo.Rectangle;
|
||||||
|
import org.apache.lucene.index.PointValues.Relation;
|
||||||
import org.apache.lucene.search.MultiTermQuery;
|
import org.apache.lucene.search.MultiTermQuery;
|
||||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||||
import org.apache.lucene.util.SloppyMath;
|
import org.apache.lucene.util.SloppyMath;
|
||||||
|
@ -28,27 +29,15 @@ import org.apache.lucene.util.SloppyMath;
|
||||||
final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
||||||
private final GeoPointDistanceQuery distanceQuery;
|
private final GeoPointDistanceQuery distanceQuery;
|
||||||
private final double centerLon;
|
private final double centerLon;
|
||||||
|
|
||||||
// optimization, maximum partial haversin needed to be a candidate
|
|
||||||
private final double maxPartialDistance;
|
|
||||||
|
|
||||||
// optimization, used for detecting axis cross
|
// optimization, used for detecting axis cross
|
||||||
final double axisLat;
|
final double axisLat;
|
||||||
|
|
||||||
GeoPointDistanceQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointDistanceQuery q,
|
GeoPointDistanceQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointDistanceQuery q,
|
||||||
final double centerLonUnwrapped, final Rectangle bbox) {
|
final double centerLonUnwrapped, final Rectangle bbox) {
|
||||||
super(field, termEncoding, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
super(field, termEncoding, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
||||||
distanceQuery = q;
|
distanceQuery = q;
|
||||||
centerLon = centerLonUnwrapped;
|
centerLon = centerLonUnwrapped;
|
||||||
|
|
||||||
// unless our box is crazy, we can use this bound
|
|
||||||
// to reject edge cases faster in postFilter()
|
|
||||||
if (bbox.maxLon - centerLon < 90 && centerLon - bbox.minLon < 90) {
|
|
||||||
maxPartialDistance = Math.max(SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, distanceQuery.centerLat, bbox.maxLon),
|
|
||||||
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, bbox.maxLat, centerLon));
|
|
||||||
} else {
|
|
||||||
maxPartialDistance = Double.POSITIVE_INFINITY;
|
|
||||||
}
|
|
||||||
axisLat = Rectangle.axisLat(distanceQuery.centerLat, distanceQuery.radiusMeters);
|
axisLat = Rectangle.axisLat(distanceQuery.centerLat, distanceQuery.radiusMeters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +93,35 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
||||||
return cellCrosses(minLat, maxLat, minLon, maxLon);
|
return cellCrosses(minLat, maxLat, minLon, maxLon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Relation relate(final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||||
|
// bounding check
|
||||||
|
if (cellIntersectsMBR(minLat, maxLat, minLon, maxLon) == false) {
|
||||||
|
return Relation.CELL_OUTSIDE_QUERY;
|
||||||
|
}
|
||||||
|
if ((centerLon < minLon || centerLon > maxLon) && (axisLat + Rectangle.AXISLAT_ERROR < minLat
|
||||||
|
|| axisLat- Rectangle.AXISLAT_ERROR > maxLat)) {
|
||||||
|
if (SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, minLat, minLon) > distanceQuery.sortKey &&
|
||||||
|
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, minLat, maxLon) > distanceQuery.sortKey &&
|
||||||
|
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, maxLat, minLon) > distanceQuery.sortKey &&
|
||||||
|
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, maxLat, maxLon) > distanceQuery.sortKey) {
|
||||||
|
return Relation.CELL_OUTSIDE_QUERY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLon - centerLon < 90 && centerLon - minLon < 90 &&
|
||||||
|
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, minLat, minLon) <= distanceQuery.sortKey &&
|
||||||
|
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, minLat, maxLon) <= distanceQuery.sortKey &&
|
||||||
|
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, maxLat, minLon) <= distanceQuery.sortKey &&
|
||||||
|
SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, maxLat, maxLon) <= distanceQuery.sortKey) {
|
||||||
|
// we are fully enclosed, collect everything within this subtree
|
||||||
|
return Relation.CELL_INSIDE_QUERY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Relation.CELL_CROSSES_QUERY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The two-phase query approach. The parent {@link GeoPointTermsEnum} class matches
|
* The two-phase query approach. The parent {@link GeoPointTermsEnum} class matches
|
||||||
* encoded terms that fall within the minimum bounding box of the point-radius circle. Those documents that pass
|
* encoded terms that fall within the minimum bounding box of the point-radius circle. Those documents that pass
|
||||||
|
@ -119,12 +137,12 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
||||||
|
|
||||||
// first check the partial distance, if its more than that, it can't be <= radiusMeters
|
// first check the partial distance, if its more than that, it can't be <= radiusMeters
|
||||||
double h1 = SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, lat, lon);
|
double h1 = SloppyMath.haversinSortKey(distanceQuery.centerLat, centerLon, lat, lon);
|
||||||
if (h1 > maxPartialDistance) {
|
if (h1 <= distanceQuery.sortKey) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fully confirm with part 2:
|
// fully confirm with part 2:
|
||||||
return SloppyMath.haversinMeters(h1) <= distanceQuery.radiusMeters;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.spatial.geopoint.search;
|
package org.apache.lucene.spatial.geopoint.search;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.PointValues.Relation;
|
||||||
import org.apache.lucene.search.MultiTermQuery;
|
import org.apache.lucene.search.MultiTermQuery;
|
||||||
import org.apache.lucene.util.SloppyMath;
|
import org.apache.lucene.util.SloppyMath;
|
||||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||||
|
@ -96,6 +97,19 @@ class GeoPointInBBoxQueryImpl extends GeoPointMultiTermQuery {
|
||||||
return cellIntersectsMBR(minLat, maxLat, minLon, maxLon);
|
return cellIntersectsMBR(minLat, maxLat, minLon, maxLon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Relation relate(final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||||
|
if (GeoRelationUtils.rectCrosses(minLat, maxLat, minLon, maxLon, GeoPointInBBoxQueryImpl.this.minLat,
|
||||||
|
GeoPointInBBoxQueryImpl.this.maxLat, GeoPointInBBoxQueryImpl.this.minLon, GeoPointInBBoxQueryImpl.this.maxLon)) {
|
||||||
|
return Relation.CELL_CROSSES_QUERY;
|
||||||
|
} else if (GeoRelationUtils.rectWithin(minLat, maxLat, minLon, maxLon, GeoPointInBBoxQueryImpl.this.minLat,
|
||||||
|
GeoPointInBBoxQueryImpl.this.maxLat,
|
||||||
|
GeoPointInBBoxQueryImpl.this.minLon, GeoPointInBBoxQueryImpl.this.maxLon)) {
|
||||||
|
return Relation.CELL_INSIDE_QUERY;
|
||||||
|
}
|
||||||
|
return Relation.CELL_OUTSIDE_QUERY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean postFilter(final double lat, final double lon) {
|
protected boolean postFilter(final double lat, final double lon) {
|
||||||
return GeoRelationUtils.pointInRectPrecise(lat, lon, minLat, maxLat, minLon, maxLon);
|
return GeoRelationUtils.pointInRectPrecise(lat, lon, minLat, maxLat, minLon, maxLon);
|
||||||
|
|
|
@ -72,6 +72,11 @@ final class GeoPointInPolygonQueryImpl extends GeoPointInBBoxQueryImpl {
|
||||||
return polygons.relate(minLat, maxLat, minLon, maxLon) != Relation.CELL_OUTSIDE_QUERY;
|
return polygons.relate(minLat, maxLat, minLon, maxLon) != Relation.CELL_OUTSIDE_QUERY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Relation relate(final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||||
|
return polygons.relate(minLat, maxLat, minLon, maxLon);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The two-phase query approach. The parent
|
* The two-phase query approach. The parent
|
||||||
* {@link org.apache.lucene.spatial.geopoint.search.GeoPointTermsEnum#accept} method is called to match
|
* {@link org.apache.lucene.spatial.geopoint.search.GeoPointTermsEnum#accept} method is called to match
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.lucene.spatial.geopoint.search;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.PointValues.Relation;
|
||||||
import org.apache.lucene.index.Terms;
|
import org.apache.lucene.index.Terms;
|
||||||
import org.apache.lucene.index.TermsEnum;
|
import org.apache.lucene.index.TermsEnum;
|
||||||
import org.apache.lucene.search.MultiTermQuery;
|
import org.apache.lucene.search.MultiTermQuery;
|
||||||
|
@ -28,7 +29,7 @@ import org.apache.lucene.util.AttributeSource;
|
||||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||||
import org.apache.lucene.spatial.util.GeoRelationUtils;
|
import org.apache.lucene.spatial.util.GeoRelationUtils;
|
||||||
import org.apache.lucene.geo.GeoUtils;
|
import org.apache.lucene.util.BitUtil;
|
||||||
import org.apache.lucene.util.SloppyMath;
|
import org.apache.lucene.util.SloppyMath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,9 +41,14 @@ import org.apache.lucene.util.SloppyMath;
|
||||||
abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
||||||
// simple bounding box optimization - no objects used to avoid dependencies
|
// simple bounding box optimization - no objects used to avoid dependencies
|
||||||
protected final double minLon;
|
protected final double minLon;
|
||||||
|
protected final long minEncoded;
|
||||||
|
protected final int minX;
|
||||||
protected final double minLat;
|
protected final double minLat;
|
||||||
|
protected final int minY;
|
||||||
protected final double maxLon;
|
protected final double maxLon;
|
||||||
|
protected final int maxX;
|
||||||
protected final double maxLat;
|
protected final double maxLat;
|
||||||
|
protected final int maxY;
|
||||||
|
|
||||||
protected final short maxShift;
|
protected final short maxShift;
|
||||||
protected final TermEncoding termEncoding;
|
protected final TermEncoding termEncoding;
|
||||||
|
@ -55,10 +61,13 @@ abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
||||||
public GeoPointMultiTermQuery(String field, final TermEncoding termEncoding, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
public GeoPointMultiTermQuery(String field, final TermEncoding termEncoding, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||||
super(field);
|
super(field);
|
||||||
|
|
||||||
GeoUtils.checkLatitude(minLat);
|
this.minEncoded = GeoPointField.encodeLatLon(minLat, minLon);
|
||||||
GeoUtils.checkLatitude(maxLat);
|
final long maxEncoded = GeoPointField.encodeLatLon(maxLat, maxLon);
|
||||||
GeoUtils.checkLongitude(minLon);
|
|
||||||
GeoUtils.checkLongitude(maxLon);
|
this.minX = (int)BitUtil.deinterleave(minEncoded);
|
||||||
|
this.maxX = (int)BitUtil.deinterleave(maxEncoded);
|
||||||
|
this.minY = (int)BitUtil.deinterleave(minEncoded >>> 1);
|
||||||
|
this.maxY = (int)BitUtil.deinterleave(maxEncoded >>> 1);
|
||||||
|
|
||||||
this.minLat = minLat;
|
this.minLat = minLat;
|
||||||
this.maxLat = maxLat;
|
this.maxLat = maxLat;
|
||||||
|
@ -128,6 +137,14 @@ abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
||||||
geoPointQuery.minLon, geoPointQuery.maxLon);
|
geoPointQuery.minLon, geoPointQuery.maxLon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** uses encoded values to check whether quad cell intersects the shape bounding box */
|
||||||
|
protected boolean cellIntersectsMBR(final long min, final long max) {
|
||||||
|
return !(Integer.compareUnsigned((int)BitUtil.deinterleave(max), geoPointQuery.minX) < 0
|
||||||
|
|| Integer.compareUnsigned((int)BitUtil.deinterleave(min), geoPointQuery.maxX) > 0
|
||||||
|
|| Integer.compareUnsigned((int)BitUtil.deinterleave(max >>> 1), geoPointQuery.minY) < 0
|
||||||
|
|| Integer.compareUnsigned((int)BitUtil.deinterleave(min >>> 1), geoPointQuery.maxY) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether quad-cell contains the bounding box of this shape
|
* Return whether quad-cell contains the bounding box of this shape
|
||||||
*/
|
*/
|
||||||
|
@ -151,6 +168,8 @@ abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
||||||
*/
|
*/
|
||||||
abstract protected boolean cellIntersectsShape(final double minLat, final double maxLat, final double minLon, final double maxLon);
|
abstract protected boolean cellIntersectsShape(final double minLat, final double maxLat, final double minLon, final double maxLon);
|
||||||
|
|
||||||
|
abstract protected Relation relate(final double minLat, final double maxLat, final double minLon, final double maxLon);
|
||||||
|
|
||||||
abstract protected boolean postFilter(final double lat, final double lon);
|
abstract protected boolean postFilter(final double lat, final double lon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
package org.apache.lucene.spatial.geopoint.search;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
@ -16,6 +14,7 @@ package org.apache.lucene.spatial.geopoint.search;
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package org.apache.lucene.spatial.geopoint.search;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -41,6 +40,7 @@ import static org.apache.lucene.spatial.geopoint.document.GeoPointField.BITS;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
final class GeoPointNumericTermsEnum extends GeoPointTermsEnum {
|
final class GeoPointNumericTermsEnum extends GeoPointTermsEnum {
|
||||||
private final List<Range> rangeBounds = new LinkedList<>();
|
private final List<Range> rangeBounds = new LinkedList<>();
|
||||||
|
private final BytesRefBuilder nextSubRangeBRB = new BytesRefBuilder();
|
||||||
|
|
||||||
// detail level should be a factor of PRECISION_STEP limiting the depth of recursion (and number of ranges)
|
// detail level should be a factor of PRECISION_STEP limiting the depth of recursion (and number of ranges)
|
||||||
private final short DETAIL_LEVEL;
|
private final short DETAIL_LEVEL;
|
||||||
|
@ -102,16 +102,16 @@ final class GeoPointNumericTermsEnum extends GeoPointTermsEnum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final BytesRef peek() {
|
protected final BytesRef peek() {
|
||||||
rangeBounds.get(0).fillBytesRef(this.nextSubRangeBRB);
|
Range range = rangeBounds.get(0);
|
||||||
|
LegacyNumericUtils.longToPrefixCoded(range.start, range.shift, this.nextSubRangeBRB);
|
||||||
return nextSubRangeBRB.get();
|
return nextSubRangeBRB.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void nextRange() {
|
protected void nextRange() {
|
||||||
currentRange = rangeBounds.remove(0);
|
currentRange = rangeBounds.remove(0);
|
||||||
super.nextRange();
|
LegacyNumericUtils.longToPrefixCoded(currentRange.start, currentRange.shift, currentCellBRB);
|
||||||
|
currentCell = currentCellBRB.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -136,27 +136,33 @@ final class GeoPointNumericTermsEnum extends GeoPointTermsEnum {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The two-phase query approach. {@link #nextSeekTerm} is called to obtain the next term that matches a numeric
|
||||||
|
* range of the bounding box. Those terms that pass the initial range filter are then compared against the
|
||||||
|
* decoded min/max latitude and longitude values of the bounding box only if the range is not a "boundary" range
|
||||||
|
* (e.g., a range that straddles the boundary of the bbox).
|
||||||
|
* @param term term for candidate document
|
||||||
|
* @return match status
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected AcceptStatus accept(BytesRef term) {
|
||||||
|
// validate value is in range
|
||||||
|
while (currentCell == null || term.compareTo(currentCell) > 0) {
|
||||||
|
if (hasNext() == false) {
|
||||||
|
return AcceptStatus.END;
|
||||||
|
}
|
||||||
|
// peek next sub-range, only seek if the current term is smaller than next lower bound
|
||||||
|
if (term.compareTo(peek()) < 0) {
|
||||||
|
return AcceptStatus.NO_AND_SEEK;
|
||||||
|
}
|
||||||
|
nextRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
return AcceptStatus.YES;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final boolean hasNext() {
|
protected final boolean hasNext() {
|
||||||
return rangeBounds.isEmpty() == false;
|
return rangeBounds.isEmpty() == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal class to represent a range along the space filling curve
|
|
||||||
*/
|
|
||||||
protected final class Range extends BaseRange {
|
|
||||||
Range(final long lower, final short shift, boolean boundary) {
|
|
||||||
super(lower, shift, boundary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode as a BytesRef using a reusable object. This allows us to lazily create the BytesRef (which is
|
|
||||||
* quite expensive), only when we need it.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void fillBytesRef(BytesRefBuilder result) {
|
|
||||||
assert result != null;
|
|
||||||
LegacyNumericUtils.longToPrefixCoded(start, shift, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
package org.apache.lucene.spatial.geopoint.search;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
@ -16,12 +14,15 @@ package org.apache.lucene.spatial.geopoint.search;
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package org.apache.lucene.spatial.geopoint.search;
|
||||||
|
|
||||||
|
import org.apache.lucene.index.PointValues;
|
||||||
import org.apache.lucene.index.TermsEnum;
|
import org.apache.lucene.index.TermsEnum;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.apache.lucene.util.BytesRefBuilder;
|
|
||||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||||
|
|
||||||
|
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.decodeLatitude;
|
||||||
|
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.decodeLongitude;
|
||||||
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.geoCodedToPrefixCoded;
|
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.geoCodedToPrefixCoded;
|
||||||
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.prefixCodedToGeoCoded;
|
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.prefixCodedToGeoCoded;
|
||||||
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.getPrefixCodedShift;
|
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.getPrefixCodedShift;
|
||||||
|
@ -37,199 +38,126 @@ import static org.apache.lucene.spatial.geopoint.document.GeoPointField.getPrefi
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
final class GeoPointPrefixTermsEnum extends GeoPointTermsEnum {
|
final class GeoPointPrefixTermsEnum extends GeoPointTermsEnum {
|
||||||
private final long start;
|
|
||||||
|
|
||||||
private short shift;
|
private short shift;
|
||||||
|
|
||||||
// current range as long
|
// current range as long
|
||||||
private long currStart;
|
private long start;
|
||||||
private long currEnd;
|
private long end;
|
||||||
|
|
||||||
private final Range nextRange = new Range(-1, shift, true);
|
|
||||||
|
|
||||||
private boolean hasNext = false;
|
private boolean hasNext = false;
|
||||||
|
|
||||||
private boolean withinOnly = false;
|
|
||||||
private long lastWithin;
|
|
||||||
|
|
||||||
public GeoPointPrefixTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
|
public GeoPointPrefixTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
|
||||||
super(tenum, query);
|
super(tenum, query);
|
||||||
this.start = GeoPointField.encodeLatLon(query.minLat, query.minLon);
|
|
||||||
this.currentRange = new Range(-1, shift, true);
|
this.currentRange = new Range(-1, shift, true);
|
||||||
// start shift at maxShift value (from computeMaxShift)
|
// start shift at maxShift value (from computeMaxShift)
|
||||||
this.shift = maxShift;
|
this.shift = maxShift;
|
||||||
final long mask = (1L << shift) - 1;
|
final long mask = (1L << shift) - 1;
|
||||||
this.currStart = start & ~mask;
|
this.start = query.minEncoded & ~mask;
|
||||||
this.currEnd = currStart | mask;
|
this.end = start | mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean within(final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
private boolean nextRelation() {
|
||||||
return relationImpl.cellWithin(minLat, maxLat, minLon, maxLon);
|
PointValues.Relation relation;
|
||||||
}
|
|
||||||
|
|
||||||
private boolean boundary(final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
|
||||||
return shift == maxShift && relationImpl.cellIntersectsShape(minLat, maxLat, minLon, maxLon);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean nextWithin() {
|
|
||||||
if (withinOnly == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
currStart += (1L << shift);
|
|
||||||
setNextRange(false);
|
|
||||||
currentRange.set(nextRange);
|
|
||||||
hasNext = true;
|
|
||||||
|
|
||||||
withinOnly = lastWithin != currStart;
|
|
||||||
if (withinOnly == false) advanceVariables();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void nextRelation() {
|
|
||||||
double minLon = GeoPointField.decodeLongitude(currStart);
|
|
||||||
double minLat = GeoPointField.decodeLatitude(currStart);
|
|
||||||
double maxLon;
|
|
||||||
double maxLat;
|
|
||||||
boolean isWithin;
|
|
||||||
do {
|
do {
|
||||||
maxLon = GeoPointField.decodeLongitude(currEnd);
|
|
||||||
maxLat = GeoPointField.decodeLatitude(currEnd);
|
|
||||||
|
|
||||||
isWithin = false;
|
|
||||||
// within or a boundary
|
// within or a boundary
|
||||||
if (boundary(minLat, maxLat, minLon, maxLon) == true) {
|
if ((shift % GeoPointField.PRECISION_STEP) == 0 &&
|
||||||
isWithin = within(minLat, maxLat, minLon, maxLon);
|
(relation = relationImpl.relate(decodeLatitude(start), decodeLatitude(end),
|
||||||
final int m;
|
decodeLongitude(start), decodeLongitude(end))) != PointValues.Relation.CELL_OUTSIDE_QUERY) {
|
||||||
if (isWithin == false || (m = shift % GeoPointField.PRECISION_STEP) == 0) {
|
// if at max depth or cell completely within
|
||||||
setNextRange(isWithin == false);
|
if (shift == maxShift || relation == PointValues.Relation.CELL_INSIDE_QUERY) {
|
||||||
|
setRange(relation == PointValues.Relation.CELL_CROSSES_QUERY);
|
||||||
advanceVariables();
|
advanceVariables();
|
||||||
break;
|
return true;
|
||||||
} else if (shift < 54) {
|
|
||||||
withinOnly = true;
|
|
||||||
shift = (short)(shift - m);
|
|
||||||
lastWithin = currEnd & ~((1L << shift) - 1);
|
|
||||||
setNextRange(false);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// within cell but not at a depth factor of PRECISION_STEP
|
// within cell but not at a depth factor of PRECISION_STEP
|
||||||
if (isWithin == true || (relationImpl.cellIntersectsMBR(minLat, maxLat, minLon, maxLon) == true && shift != maxShift)) {
|
if (shift != maxShift && relationImpl.cellIntersectsMBR(start, end) == true) {
|
||||||
// descend: currStart need not change since shift handles end of range
|
// descend: start need not change since shift handles end of range
|
||||||
currEnd = currStart | (1L<<--shift) - 1;
|
end = start | (1L<<--shift) - 1;
|
||||||
} else {
|
} else {
|
||||||
advanceVariables();
|
advanceVariables();
|
||||||
minLon = GeoPointField.decodeLongitude(currStart);
|
|
||||||
minLat = GeoPointField.decodeLatitude(currStart);
|
|
||||||
}
|
}
|
||||||
} while(shift < 63);
|
} while(shift < 62);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setNextRange(final boolean boundary) {
|
private void setRange(final boolean boundary) {
|
||||||
nextRange.start = currStart;
|
currentRange.start = start;
|
||||||
nextRange.shift = shift;
|
currentRange.shift = shift;
|
||||||
nextRange.boundary = boundary;
|
currentRange.boundary = boundary;
|
||||||
|
hasNext = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void advanceVariables() {
|
private void advanceVariables() {
|
||||||
/** set next variables */
|
/** set next variables */
|
||||||
long shiftMask = 1L << shift;
|
long shiftMask = 1L << shift;
|
||||||
// pop-up if shift bit is set
|
// pop-up if shift bit is set
|
||||||
while ( (currStart & shiftMask) == shiftMask) {
|
while ( (start & shiftMask) == shiftMask) {
|
||||||
shiftMask = 1L << ++shift;
|
shiftMask = 1L << ++shift;
|
||||||
}
|
}
|
||||||
final long shiftMOne = shiftMask - 1;
|
final long shiftMOne = shiftMask - 1;
|
||||||
currStart = currStart & ~shiftMOne | shiftMask;
|
start = start & ~shiftMOne | shiftMask;
|
||||||
currEnd = currStart | shiftMOne;
|
end = start | shiftMOne;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final BytesRef peek() {
|
|
||||||
nextRange.fillBytesRef(nextSubRangeBRB);
|
|
||||||
return super.peek();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void seek(long term, short res) {
|
protected void seek(long term, short res) {
|
||||||
if (term < currStart && res < maxShift) {
|
if (term < start && res < maxShift) {
|
||||||
throw new IllegalArgumentException("trying to seek backwards");
|
throw new IllegalArgumentException("trying to seek backwards");
|
||||||
} else if (term == currStart) {
|
} else if (term == start) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
shift = res;
|
shift = res;
|
||||||
currStart = term;
|
start = term;
|
||||||
currEnd = currStart | ((1L<<shift)-1);
|
end = start | ((1L<<shift)-1);
|
||||||
withinOnly = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void nextRange() {
|
|
||||||
hasNext = false;
|
|
||||||
super.nextRange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final boolean hasNext() {
|
protected final boolean hasNext() {
|
||||||
if (hasNext == true || nextWithin()) {
|
if (hasNext == false) {
|
||||||
return true;
|
return nextRelation();
|
||||||
}
|
}
|
||||||
nextRelation();
|
return true;
|
||||||
if (currentRange.compareTo(nextRange) != 0) {
|
|
||||||
currentRange.set(nextRange);
|
|
||||||
return (hasNext = true);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final BytesRef nextSeekTerm(BytesRef term) {
|
protected final BytesRef nextSeekTerm(BytesRef term) {
|
||||||
while (hasNext()) {
|
if (hasNext() == false) {
|
||||||
nextRange();
|
return null;
|
||||||
if (term == null) {
|
|
||||||
return currentCell;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int comparison = term.compareTo(currentCell);
|
|
||||||
if (comparison > 0) {
|
|
||||||
seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return currentCell;
|
|
||||||
}
|
}
|
||||||
|
geoCodedToPrefixCoded(currentRange.start, currentRange.shift, currentCellBRB);
|
||||||
// no more sub-range enums available
|
hasNext = false;
|
||||||
return null;
|
return currentCellBRB.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AcceptStatus accept(BytesRef term) {
|
protected AcceptStatus accept(BytesRef term) {
|
||||||
|
final long encodedTerm = prefixCodedToGeoCoded(term);
|
||||||
|
final short termShift = (short)(64-getPrefixCodedShift(term));
|
||||||
// range < term or range is null
|
// range < term or range is null
|
||||||
while (currentCell == null || term.compareTo(currentCell) > 0) {
|
while (currentRange.compare(encodedTerm, termShift) < 0) {
|
||||||
// no more ranges, be gone
|
// no more ranges, be gone
|
||||||
if (hasNext() == false) {
|
if (hasNext() == false) {
|
||||||
return AcceptStatus.END;
|
return AcceptStatus.END;
|
||||||
}
|
}
|
||||||
|
|
||||||
// peek next range, if the range > term then seek
|
// peek next range, if the range > term then seek
|
||||||
final int peekCompare = term.compareTo(peek());
|
final int peekCompare = currentRange.compare(encodedTerm, termShift);
|
||||||
if (peekCompare < 0) {
|
if (peekCompare > 0) {
|
||||||
return AcceptStatus.NO_AND_SEEK;
|
return AcceptStatus.NO_AND_SEEK;
|
||||||
} else if (peekCompare > 0) {
|
} else if (peekCompare < 0) {
|
||||||
seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
|
seek(encodedTerm, termShift);
|
||||||
}
|
}
|
||||||
nextRange();
|
hasNext = false;
|
||||||
}
|
}
|
||||||
return AcceptStatus.YES;
|
return AcceptStatus.YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final class Range extends BaseRange {
|
@Override
|
||||||
public Range(final long start, final short res, final boolean boundary) {
|
public boolean boundaryTerm() {
|
||||||
super(start, res, boundary);
|
if (currentRange.start == -1) {
|
||||||
}
|
throw new IllegalStateException("GeoPointTermsEnum empty or not initialized");
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void fillBytesRef(BytesRefBuilder result) {
|
|
||||||
assert result != null;
|
|
||||||
geoCodedToPrefixCoded(start, shift, result);
|
|
||||||
}
|
}
|
||||||
|
return currentRange.boundary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,9 @@ import org.apache.lucene.spatial.geopoint.search.GeoPointMultiTermQuery.CellComp
|
||||||
abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
||||||
protected final short maxShift;
|
protected final short maxShift;
|
||||||
|
|
||||||
protected BaseRange currentRange;
|
protected Range currentRange;
|
||||||
protected BytesRef currentCell;
|
protected BytesRef currentCell;
|
||||||
protected final BytesRefBuilder currentCellBRB = new BytesRefBuilder();
|
protected final BytesRefBuilder currentCellBRB = new BytesRefBuilder();
|
||||||
protected final BytesRefBuilder nextSubRangeBRB = new BytesRefBuilder();
|
|
||||||
|
|
||||||
protected final CellComparator relationImpl;
|
protected final CellComparator relationImpl;
|
||||||
|
|
||||||
|
@ -61,43 +60,8 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
||||||
return currentRange.boundary;
|
return currentRange.boundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BytesRef peek() {
|
|
||||||
return nextSubRangeBRB.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected boolean hasNext();
|
abstract protected boolean hasNext();
|
||||||
|
|
||||||
protected void nextRange() {
|
|
||||||
currentRange.fillBytesRef(currentCellBRB);
|
|
||||||
currentCell = currentCellBRB.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The two-phase query approach. {@link #nextSeekTerm} is called to obtain the next term that matches a numeric
|
|
||||||
* range of the bounding box. Those terms that pass the initial range filter are then compared against the
|
|
||||||
* decoded min/max latitude and longitude values of the bounding box only if the range is not a "boundary" range
|
|
||||||
* (e.g., a range that straddles the boundary of the bbox).
|
|
||||||
* @param term term for candidate document
|
|
||||||
* @return match status
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected AcceptStatus accept(BytesRef term) {
|
|
||||||
// validate value is in range
|
|
||||||
while (currentCell == null || term.compareTo(currentCell) > 0) {
|
|
||||||
if (hasNext() == false) {
|
|
||||||
return AcceptStatus.END;
|
|
||||||
}
|
|
||||||
// peek next sub-range, only seek if the current term is smaller than next lower bound
|
|
||||||
if (term.compareTo(peek()) < 0) {
|
|
||||||
return AcceptStatus.NO_AND_SEEK;
|
|
||||||
}
|
|
||||||
// step forward to next range without seeking, as next range is less or equal current term
|
|
||||||
nextRange();
|
|
||||||
}
|
|
||||||
|
|
||||||
return AcceptStatus.YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean postFilter(final double lat, final double lon) {
|
protected boolean postFilter(final double lat, final double lon) {
|
||||||
return relationImpl.postFilter(lat, lon);
|
return relationImpl.postFilter(lat, lon);
|
||||||
}
|
}
|
||||||
|
@ -105,25 +69,19 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
||||||
/**
|
/**
|
||||||
* Internal class to represent a range along the space filling curve
|
* Internal class to represent a range along the space filling curve
|
||||||
*/
|
*/
|
||||||
abstract class BaseRange implements Comparable<BaseRange> {
|
class Range implements Comparable<Range> {
|
||||||
protected short shift;
|
protected short shift;
|
||||||
protected long start;
|
protected long start;
|
||||||
protected boolean boundary;
|
protected boolean boundary;
|
||||||
|
|
||||||
BaseRange(final long lower, final short shift, boolean boundary) {
|
Range(final long lower, final short shift, boolean boundary) {
|
||||||
this.boundary = boundary;
|
this.boundary = boundary;
|
||||||
this.start = lower;
|
this.start = lower;
|
||||||
this.shift = shift;
|
this.shift = shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode as a BytesRef using a reusable object. This allows us to lazily create the BytesRef (which is
|
|
||||||
* quite expensive), only when we need it.
|
|
||||||
*/
|
|
||||||
abstract protected void fillBytesRef(BytesRefBuilder result);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(BaseRange other) {
|
public int compareTo(Range other) {
|
||||||
final int result = Short.compare(this.shift, other.shift);
|
final int result = Short.compare(this.shift, other.shift);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return Long.compare(this.start, other.start);
|
return Long.compare(this.start, other.start);
|
||||||
|
@ -131,10 +89,18 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void set(BaseRange other) {
|
protected void set(Range other) {
|
||||||
this.start = other.start;
|
this.start = other.start;
|
||||||
this.shift = other.shift;
|
this.shift = other.shift;
|
||||||
this.boundary = other.boundary;
|
this.boundary = other.boundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected int compare(long encoded, short shift) {
|
||||||
|
final int result = Long.compare(this.start, encoded);
|
||||||
|
if (result == 0) {
|
||||||
|
return Short.compare(shift, this.shift);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue