mirror of https://github.com/apache/lucene.git
LUCENE-7328: Remove LegacyNumericEncoding from GeoPointField
This commit is contained in:
parent
816fae9622
commit
e4db256d3f
|
@ -4,7 +4,14 @@ For more information on past and future Lucene versions, please see:
|
|||
http://s.apache.org/luceneversions
|
||||
|
||||
======================= Lucene 7.0.0 =======================
|
||||
(No Changes)
|
||||
|
||||
Bug Fixes
|
||||
|
||||
Improvements
|
||||
|
||||
Other
|
||||
|
||||
* LUCENE-7328: Remove LegacyNumericEncoding from GeoPointField. (Nick Knize)
|
||||
|
||||
======================= Lucene 6.2.0 =======================
|
||||
|
||||
|
|
|
@ -46,9 +46,6 @@ import static org.apache.lucene.geo.GeoUtils.MIN_LON_INCL;
|
|||
* see {@link org.apache.lucene.spatial.geopoint.search.GeoPointInBBoxQuery}, {@link org.apache.lucene.spatial.geopoint.search.GeoPointInPolygonQuery},
|
||||
* or {@link org.apache.lucene.spatial.geopoint.search.GeoPointDistanceQuery}
|
||||
*
|
||||
* NOTE: This indexes only high precision encoded terms which may result in visiting a high number
|
||||
* of terms for large queries. See LUCENE-6481 for a future improvement.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public final class GeoPointField extends Field {
|
||||
|
@ -68,81 +65,31 @@ public final class GeoPointField extends Field {
|
|||
*/
|
||||
private static final int BUF_SIZE_LONG = 28/8 + 1;
|
||||
|
||||
/**
|
||||
* <b>Expert:</b> Optional flag to select term encoding for GeoPointField types
|
||||
*/
|
||||
public enum TermEncoding {
|
||||
/**
|
||||
* encodes prefix terms only resulting in a small index and faster queries - use with
|
||||
* {@code GeoPointTokenStream}
|
||||
*/
|
||||
PREFIX,
|
||||
/**
|
||||
* @deprecated encodes prefix and full resolution terms - use with
|
||||
* {@link org.apache.lucene.analysis.LegacyNumericTokenStream}
|
||||
*/
|
||||
@Deprecated
|
||||
NUMERIC
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Type for a GeoPointField that is not stored:
|
||||
* normalization factors, frequencies, and positions are omitted.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final FieldType NUMERIC_TYPE_NOT_STORED = new FieldType();
|
||||
static {
|
||||
NUMERIC_TYPE_NOT_STORED.setTokenized(false);
|
||||
NUMERIC_TYPE_NOT_STORED.setOmitNorms(true);
|
||||
NUMERIC_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
|
||||
NUMERIC_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
|
||||
NUMERIC_TYPE_NOT_STORED.setNumericType(FieldType.LegacyNumericType.LONG);
|
||||
NUMERIC_TYPE_NOT_STORED.setNumericPrecisionStep(PRECISION_STEP);
|
||||
NUMERIC_TYPE_NOT_STORED.freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Type for a stored GeoPointField:
|
||||
* normalization factors, frequencies, and positions are omitted.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final FieldType NUMERIC_TYPE_STORED = new FieldType();
|
||||
static {
|
||||
NUMERIC_TYPE_STORED.setTokenized(false);
|
||||
NUMERIC_TYPE_STORED.setOmitNorms(true);
|
||||
NUMERIC_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
|
||||
NUMERIC_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
|
||||
NUMERIC_TYPE_STORED.setNumericType(FieldType.LegacyNumericType.LONG);
|
||||
NUMERIC_TYPE_STORED.setNumericPrecisionStep(PRECISION_STEP);
|
||||
NUMERIC_TYPE_STORED.setStored(true);
|
||||
NUMERIC_TYPE_STORED.freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
* Type for a GeoPointField that is not stored:
|
||||
* normalization factors, frequencies, and positions are omitted.
|
||||
*/
|
||||
public static final FieldType PREFIX_TYPE_NOT_STORED = new FieldType();
|
||||
public static final FieldType TYPE_NOT_STORED = new FieldType();
|
||||
static {
|
||||
PREFIX_TYPE_NOT_STORED.setTokenized(false);
|
||||
PREFIX_TYPE_NOT_STORED.setOmitNorms(true);
|
||||
PREFIX_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
|
||||
PREFIX_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
|
||||
PREFIX_TYPE_NOT_STORED.freeze();
|
||||
TYPE_NOT_STORED.setTokenized(false);
|
||||
TYPE_NOT_STORED.setOmitNorms(true);
|
||||
TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
|
||||
TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
|
||||
TYPE_NOT_STORED.freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
* Type for a stored GeoPointField:
|
||||
* normalization factors, frequencies, and positions are omitted.
|
||||
*/
|
||||
public static final FieldType PREFIX_TYPE_STORED = new FieldType();
|
||||
public static final FieldType TYPE_STORED = new FieldType();
|
||||
static {
|
||||
PREFIX_TYPE_STORED.setTokenized(false);
|
||||
PREFIX_TYPE_STORED.setOmitNorms(true);
|
||||
PREFIX_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
|
||||
PREFIX_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
|
||||
PREFIX_TYPE_STORED.setStored(true);
|
||||
PREFIX_TYPE_STORED.freeze();
|
||||
TYPE_STORED.setTokenized(false);
|
||||
TYPE_STORED.setOmitNorms(true);
|
||||
TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
|
||||
TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
|
||||
TYPE_STORED.setStored(true);
|
||||
TYPE_STORED.freeze();
|
||||
}
|
||||
|
||||
/** Creates a stored or un-stored GeoPointField
|
||||
|
@ -156,28 +103,13 @@ public final class GeoPointField extends Field {
|
|||
this(name, latitude, longitude, getFieldType(stored));
|
||||
}
|
||||
|
||||
/** Creates a stored or un-stored GeoPointField using the specified {@link TermEncoding} method
|
||||
* @param name field name
|
||||
* @param latitude latitude double value [-90.0 : 90.0]
|
||||
* @param longitude longitude double value [-180.0 : 180.0]
|
||||
* @param termEncoding encoding type to use ({@link TermEncoding#NUMERIC} Terms, or {@link TermEncoding#PREFIX} only Terms)
|
||||
* @param stored Store.YES if the content should also be stored
|
||||
* @throws IllegalArgumentException if the field name is null.
|
||||
*/
|
||||
@Deprecated
|
||||
public GeoPointField(String name, double latitude, double longitude, TermEncoding termEncoding, Store stored) {
|
||||
this(name, latitude, longitude, getFieldType(termEncoding, stored));
|
||||
}
|
||||
|
||||
/** Expert: allows you to customize the {@link
|
||||
* FieldType}.
|
||||
* @param name field name
|
||||
* @param latitude latitude double value [-90.0 : 90.0]
|
||||
* @param longitude longitude double value [-180.0 : 180.0]
|
||||
* @param type customized field type: must have {@link FieldType#numericType()}
|
||||
* of {@link org.apache.lucene.document.FieldType.LegacyNumericType#LONG}.
|
||||
* @throws IllegalArgumentException if the field name or type is null, or
|
||||
* if the field type does not have a LONG numericType()
|
||||
* @param type customized field type
|
||||
* @throws IllegalArgumentException if the field name or type is null
|
||||
*/
|
||||
public GeoPointField(String name, double latitude, double longitude, FieldType type) {
|
||||
super(name, type);
|
||||
|
@ -193,12 +125,6 @@ public final class GeoPointField extends Field {
|
|||
if (type.docValuesType() != DocValuesType.SORTED_NUMERIC) {
|
||||
throw new IllegalArgumentException("type.docValuesType() must be SORTED_NUMERIC but got " + type.docValuesType());
|
||||
}
|
||||
if (type.numericType() != null) {
|
||||
// make sure numericType is a LONG
|
||||
if (type.numericType() != FieldType.LegacyNumericType.LONG) {
|
||||
throw new IllegalArgumentException("type.numericType() must be LONG but got " + type.numericType());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("type.indexOptions() must be one of NONE or DOCS but got " + type.indexOptions());
|
||||
}
|
||||
|
@ -207,20 +133,14 @@ public final class GeoPointField extends Field {
|
|||
fieldsData = encodeLatLon(latitude, longitude);
|
||||
}
|
||||
|
||||
private static FieldType getFieldType(Store stored) {
|
||||
return getFieldType(TermEncoding.PREFIX, stored);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Static helper method for returning a valid FieldType based on termEncoding and stored options
|
||||
*/
|
||||
@Deprecated
|
||||
private static FieldType getFieldType(TermEncoding termEncoding, Store stored) {
|
||||
private static FieldType getFieldType(Store stored) {
|
||||
if (stored == Store.YES) {
|
||||
return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_STORED : NUMERIC_TYPE_STORED;
|
||||
return TYPE_STORED;
|
||||
} else if (stored == Store.NO) {
|
||||
return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_NOT_STORED : NUMERIC_TYPE_NOT_STORED;
|
||||
return TYPE_NOT_STORED;
|
||||
} else {
|
||||
throw new IllegalArgumentException("stored option must be NO or YES but got " + stored);
|
||||
}
|
||||
|
@ -233,12 +153,6 @@ public final class GeoPointField extends Field {
|
|||
return null;
|
||||
}
|
||||
|
||||
// if numericType is set
|
||||
if (type.numericType() != null) {
|
||||
// return numeric encoding
|
||||
return super.tokenStream(analyzer, reuse);
|
||||
}
|
||||
|
||||
if (reuse instanceof GeoPointTokenStream == false) {
|
||||
reuse = new GeoPointTokenStream();
|
||||
}
|
||||
|
|
|
@ -35,28 +35,17 @@ import static org.apache.lucene.spatial.geopoint.document.GeoPointField.PRECISIO
|
|||
|
||||
/**
|
||||
* <b>Expert:</b> This class provides a {@link TokenStream} used by {@link GeoPointField}
|
||||
* for encoding {@link GeoPointField.TermEncoding#PREFIX} only GeoPointTerms.
|
||||
* for encoding GeoPoint terms.
|
||||
*
|
||||
* <p><i>NOTE: This is used as the default encoding unless
|
||||
* {@code GeoPointField.setNumericType(FieldType.LegacyNumericType.LONG)} is set</i></p>
|
||||
*
|
||||
* This class is similar to {@link org.apache.lucene.analysis.LegacyNumericTokenStream} but encodes terms up to a
|
||||
* a maximum of {@link #MAX_SHIFT} using a fixed precision step defined by
|
||||
* This class encodes terms up to a maximum of {@link #MAX_SHIFT} using a fixed precision step defined by
|
||||
* {@link GeoPointField#PRECISION_STEP}. This yields a total of 4 terms per GeoPoint
|
||||
* each consisting of 5 bytes (4 prefix bytes + 1 precision byte).
|
||||
*
|
||||
* <p>For best performance use the provided {@link GeoPointField#PREFIX_TYPE_NOT_STORED} or
|
||||
* {@link GeoPointField#PREFIX_TYPE_STORED}</p>
|
||||
*
|
||||
* <p>If prefix terms are used then the default GeoPoint query constructors may be used, but if
|
||||
* {@link org.apache.lucene.analysis.LegacyNumericTokenStream} is used, then be sure to pass
|
||||
* {@link GeoPointField.TermEncoding#NUMERIC} to all GeoPointQuery constructors</p>
|
||||
*
|
||||
* Here's an example usage:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* // using prefix terms
|
||||
* GeoPointField geoPointField = new GeoPointField(fieldName1, lat, lon, GeoPointField.PREFIX_TYPE_NOT_STORED);
|
||||
* GeoPointField geoPointField = new GeoPointField(fieldName1, lat, lon, GeoPointField.TYPE_NOT_STORED);
|
||||
* document.add(geoPointField);
|
||||
*
|
||||
* // query by bounding box (default uses TermEncoding.PREFIX)
|
||||
|
|
|
@ -20,7 +20,6 @@ import org.apache.lucene.index.IndexReader;
|
|||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
|
||||
|
@ -70,21 +69,12 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
* distance (in meters) from a given point
|
||||
**/
|
||||
public GeoPointDistanceQuery(final String field, final double centerLat, final double centerLon, final double radiusMeters) {
|
||||
this(field, TermEncoding.PREFIX, checkLatitude(centerLat), checkLongitude(centerLon), checkRadius(radiusMeters));
|
||||
this(field, Rectangle.fromPointDistance(centerLat, centerLon, checkRadius(radiusMeters)), centerLat, centerLon, radiusMeters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Query for all {@link org.apache.lucene.spatial.geopoint.document.GeoPointField} types within a
|
||||
* distance (in meters) from a given point. Accepts optional
|
||||
* {@link org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding} parameter
|
||||
**/
|
||||
public GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final double centerLat, final double centerLon, final double radiusMeters) {
|
||||
this(field, termEncoding, Rectangle.fromPointDistance(centerLat, centerLon, checkRadius(radiusMeters)), centerLat, centerLon, radiusMeters);
|
||||
}
|
||||
|
||||
private GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final Rectangle bbox,
|
||||
private GeoPointDistanceQuery(final String field, final Rectangle bbox,
|
||||
final double centerLat, final double centerLon, final double radiusMeters) {
|
||||
super(field, termEncoding, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
||||
super(field, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
||||
|
||||
this.centerLat = centerLat;
|
||||
this.centerLon = centerLon;
|
||||
|
@ -103,7 +93,7 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
// unwrap left
|
||||
unwrappedLon += -360.0D;
|
||||
}
|
||||
GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
|
||||
GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, this, unwrappedLon,
|
||||
new Rectangle(minLat, maxLat, GeoUtils.MIN_LON_INCL, maxLon));
|
||||
bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
|
||||
|
||||
|
@ -111,13 +101,13 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
// unwrap right
|
||||
unwrappedLon += 360.0D;
|
||||
}
|
||||
GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
|
||||
GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, this, unwrappedLon,
|
||||
new Rectangle(minLat, maxLat, minLon, GeoUtils.MAX_LON_INCL));
|
||||
bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
|
||||
|
||||
return bqb.build();
|
||||
}
|
||||
return new GeoPointDistanceQueryImpl(field, termEncoding, this, centerLon,
|
||||
return new GeoPointDistanceQueryImpl(field, this, centerLon,
|
||||
new Rectangle(this.minLat, this.maxLat, this.minLon, this.maxLon));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.apache.lucene.spatial.geopoint.search;
|
|||
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
|
||||
/** Package private implementation for the public facing GeoPointDistanceQuery delegate class.
|
||||
|
@ -35,9 +34,9 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
|||
// optimization, used for detecting axis cross
|
||||
final double axisLat;
|
||||
|
||||
GeoPointDistanceQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointDistanceQuery q,
|
||||
GeoPointDistanceQueryImpl(final String field, final GeoPointDistanceQuery q,
|
||||
final double centerLonUnwrapped, final Rectangle bbox) {
|
||||
super(field, termEncoding, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
||||
super(field, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
||||
distanceQuery = q;
|
||||
centerLon = centerLonUnwrapped;
|
||||
|
||||
|
|
|
@ -20,13 +20,10 @@ import org.apache.lucene.index.IndexReader;
|
|||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.FieldValueQuery;
|
||||
import org.apache.lucene.search.LegacyNumericRangeQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
|
||||
/** Implements a simple bounding box query on a GeoPoint field. This is inspired by
|
||||
* {@link LegacyNumericRangeQuery} and is implemented using a
|
||||
/** Implements a simple bounding box query on a GeoPoint field. This is implemented using a
|
||||
* two phase approach. First, candidate terms are queried using a numeric
|
||||
* range based on the morton codes of the min and max lat/lon pairs. Terms
|
||||
* passing this initial filter are passed to a final check that verifies whether
|
||||
|
@ -53,28 +50,15 @@ public class GeoPointInBBoxQuery extends Query {
|
|||
protected final double maxLat;
|
||||
/** maximum longitude value (in degrees) */
|
||||
protected final double maxLon;
|
||||
/** term encoding enum to define how the points are encoded (PREFIX or NUMERIC) */
|
||||
protected final TermEncoding termEncoding;
|
||||
|
||||
/**
|
||||
* Constructs a query for all {@link org.apache.lucene.spatial.geopoint.document.GeoPointField} types that fall within a
|
||||
* defined bounding box
|
||||
* defined bounding box.
|
||||
*/
|
||||
public GeoPointInBBoxQuery(final String field, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
this(field, TermEncoding.PREFIX, minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a query for all {@link org.apache.lucene.spatial.geopoint.document.GeoPointField} types that fall within a
|
||||
* defined bounding box. Accepts optional {@link org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding} parameter
|
||||
*/
|
||||
public GeoPointInBBoxQuery(final String field, final TermEncoding termEncoding, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("field must not be null");
|
||||
}
|
||||
if (termEncoding == null) {
|
||||
throw new IllegalArgumentException("termEncoding must not be null");
|
||||
}
|
||||
GeoUtils.checkLatitude(minLat);
|
||||
GeoUtils.checkLatitude(maxLat);
|
||||
GeoUtils.checkLongitude(minLon);
|
||||
|
@ -84,7 +68,6 @@ public class GeoPointInBBoxQuery extends Query {
|
|||
this.maxLat = maxLat;
|
||||
this.minLon = minLon;
|
||||
this.maxLon = maxLon;
|
||||
this.termEncoding = termEncoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -99,13 +82,13 @@ public class GeoPointInBBoxQuery extends Query {
|
|||
if (maxLon < minLon) {
|
||||
BooleanQuery.Builder bqb = new BooleanQuery.Builder();
|
||||
|
||||
GeoPointInBBoxQueryImpl left = new GeoPointInBBoxQueryImpl(field, termEncoding, minLat, maxLat, -180.0D, maxLon);
|
||||
GeoPointInBBoxQueryImpl left = new GeoPointInBBoxQueryImpl(field, minLat, maxLat, -180.0D, maxLon);
|
||||
bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
|
||||
GeoPointInBBoxQueryImpl right = new GeoPointInBBoxQueryImpl(field, termEncoding, minLat, maxLat, minLon, 180.0D);
|
||||
GeoPointInBBoxQueryImpl right = new GeoPointInBBoxQueryImpl(field, minLat, maxLat, minLon, 180.0D);
|
||||
bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
|
||||
return bqb.build();
|
||||
}
|
||||
return new GeoPointInBBoxQueryImpl(field, termEncoding, minLat, maxLat, minLon, maxLon);
|
||||
return new GeoPointInBBoxQueryImpl(field, minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.lucene.spatial.geopoint.search;
|
|||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.GeoRelationUtils;
|
||||
|
||||
/** Package private implementation for the public facing GeoPointInBBoxQuery delegate class.
|
||||
|
@ -36,8 +35,8 @@ class GeoPointInBBoxQueryImpl extends GeoPointMultiTermQuery {
|
|||
* @param maxLon upper longitude (x) value of the bounding box
|
||||
* @param maxLat upper latitude (y) value of the bounding box
|
||||
*/
|
||||
GeoPointInBBoxQueryImpl(final String field, final TermEncoding termEncoding, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
super(field, termEncoding, minLat, maxLat, minLon, maxLon);
|
||||
GeoPointInBBoxQueryImpl(final String field, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
super(field, minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,8 +20,6 @@ import java.util.Arrays;
|
|||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
||||
|
@ -46,47 +44,19 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQuery {
|
|||
* that fall within or on the boundary of the polygons defined by the input parameters.
|
||||
*/
|
||||
public GeoPointInPolygonQuery(String field, Polygon... polygons) {
|
||||
this(field, TermEncoding.PREFIX, polygons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new GeoPolygonQuery that will match encoded {@link org.apache.lucene.spatial.geopoint.document.GeoPointField} terms
|
||||
* that fall within or on the boundary of the polygon defined by the input parameters.
|
||||
* @deprecated Use {@link #GeoPointInPolygonQuery(String, Polygon[])}.
|
||||
*/
|
||||
@Deprecated
|
||||
public GeoPointInPolygonQuery(final String field, final double[] polyLats, final double[] polyLons) {
|
||||
this(field, TermEncoding.PREFIX, polyLats, polyLons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new GeoPolygonQuery that will match encoded {@link org.apache.lucene.spatial.geopoint.document.GeoPointField} terms
|
||||
* that fall within or on the boundary of the polygon defined by the input parameters.
|
||||
* @deprecated Use {@link #GeoPointInPolygonQuery(String, GeoPointField.TermEncoding, Polygon[])} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public GeoPointInPolygonQuery(final String field, final TermEncoding termEncoding, final double[] polyLats, final double[] polyLons) {
|
||||
this(field, termEncoding, new Polygon(polyLats, polyLons));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new GeoPolygonQuery that will match encoded {@link org.apache.lucene.spatial.geopoint.document.GeoPointField} terms
|
||||
* that fall within or on the boundary of the polygons defined by the input parameters.
|
||||
*/
|
||||
public GeoPointInPolygonQuery(String field, TermEncoding termEncoding, Polygon... polygons) {
|
||||
this(field, termEncoding, Rectangle.fromPolygon(polygons), polygons);
|
||||
this(field, Rectangle.fromPolygon(polygons), polygons);
|
||||
}
|
||||
|
||||
// internal constructor
|
||||
private GeoPointInPolygonQuery(String field, TermEncoding termEncoding, Rectangle boundingBox, Polygon... polygons) {
|
||||
super(field, termEncoding, boundingBox.minLat, boundingBox.maxLat, boundingBox.minLon, boundingBox.maxLon);
|
||||
private GeoPointInPolygonQuery(String field, Rectangle boundingBox, Polygon... polygons) {
|
||||
super(field, boundingBox.minLat, boundingBox.maxLat, boundingBox.minLon, boundingBox.maxLon);
|
||||
this.polygons = polygons.clone();
|
||||
}
|
||||
|
||||
/** throw exception if trying to change rewrite method */
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) {
|
||||
return new GeoPointInPolygonQueryImpl(field, termEncoding, this, this.minLat, this.maxLat, this.minLon, this.maxLon);
|
||||
return new GeoPointInPolygonQueryImpl(field, this, this.minLat, this.maxLat, this.minLon, this.maxLon);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.lucene.spatial.geopoint.search;
|
|||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.geo.Polygon2D;
|
||||
import org.apache.lucene.index.PointValues.Relation;
|
||||
|
||||
|
@ -31,9 +30,9 @@ final class GeoPointInPolygonQueryImpl extends GeoPointInBBoxQueryImpl {
|
|||
private final GeoPointInPolygonQuery polygonQuery;
|
||||
private final Polygon2D polygons;
|
||||
|
||||
GeoPointInPolygonQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointInPolygonQuery q,
|
||||
GeoPointInPolygonQueryImpl(final String field, final GeoPointInPolygonQuery q,
|
||||
final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
super(field, termEncoding, minLat, maxLat, minLon, maxLon);
|
||||
super(field, minLat, maxLat, minLon, maxLon);
|
||||
this.polygonQuery = Objects.requireNonNull(q);
|
||||
this.polygons = Polygon2D.create(q.polygons);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.apache.lucene.search.MultiTermQuery;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.util.AttributeSource;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.GeoRelationUtils;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
|
@ -45,14 +44,13 @@ abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
|||
protected final double maxLat;
|
||||
|
||||
protected final short maxShift;
|
||||
protected final TermEncoding termEncoding;
|
||||
protected final CellComparator cellComparator;
|
||||
|
||||
/**
|
||||
* Constructs a query matching terms that cannot be represented with a single
|
||||
* Term.
|
||||
*/
|
||||
public GeoPointMultiTermQuery(String field, final TermEncoding termEncoding, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
public GeoPointMultiTermQuery(String field, final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
super(field);
|
||||
|
||||
GeoUtils.checkLatitude(minLat);
|
||||
|
@ -66,7 +64,6 @@ abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
|||
this.maxLon = maxLon;
|
||||
|
||||
this.maxShift = computeMaxShift();
|
||||
this.termEncoding = termEncoding;
|
||||
this.cellComparator = newCellComparator();
|
||||
|
||||
this.rewriteMethod = GEO_CONSTANT_SCORE_REWRITE;
|
||||
|
@ -81,7 +78,7 @@ abstract class GeoPointMultiTermQuery extends MultiTermQuery {
|
|||
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
protected TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException {
|
||||
return GeoPointTermsEnum.newInstance(terms.iterator(), this);
|
||||
return new GeoPointTermsEnum(terms.iterator(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,162 +0,0 @@
|
|||
package org.apache.lucene.spatial.geopoint.search;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.index.TermsEnum;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.apache.lucene.util.LegacyNumericUtils;
|
||||
|
||||
import static org.apache.lucene.spatial.geopoint.document.GeoPointField.BITS;
|
||||
|
||||
/**
|
||||
* Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
|
||||
* {@link org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding#NUMERIC} method defined by
|
||||
* {@link org.apache.lucene.analysis.LegacyNumericTokenStream}. The terms are then enumerated by the
|
||||
* {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or
|
||||
* pass the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the resulting DocIdSet.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
@Deprecated
|
||||
final class GeoPointNumericTermsEnum extends GeoPointTermsEnum {
|
||||
private final List<Range> rangeBounds = new LinkedList<>();
|
||||
|
||||
// detail level should be a factor of PRECISION_STEP limiting the depth of recursion (and number of ranges)
|
||||
private final short DETAIL_LEVEL;
|
||||
|
||||
GeoPointNumericTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
|
||||
super(tenum, query);
|
||||
DETAIL_LEVEL = (short)(((BITS<<1)-this.maxShift)/2);
|
||||
computeRange(0L, (short) (((BITS) << 1) - 1));
|
||||
assert rangeBounds.isEmpty() == false;
|
||||
Collections.sort(rangeBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* entry point for recursively computing ranges
|
||||
*/
|
||||
private final void computeRange(long term, final short shift) {
|
||||
final long split = term | (0x1L<<shift);
|
||||
assert shift < 64;
|
||||
final long upperMax;
|
||||
if (shift < 63) {
|
||||
upperMax = term | ((1L << (shift+1))-1);
|
||||
} else {
|
||||
upperMax = 0xffffffffffffffffL;
|
||||
}
|
||||
final long lowerMax = split-1;
|
||||
|
||||
relateAndRecurse(term, lowerMax, shift);
|
||||
relateAndRecurse(split, upperMax, shift);
|
||||
}
|
||||
|
||||
/**
|
||||
* recurse to higher level precision cells to find ranges along the space-filling curve that fall within the
|
||||
* query box
|
||||
*
|
||||
* @param start starting value on the space-filling curve for a cell at a given res
|
||||
* @param end ending value on the space-filling curve for a cell at a given res
|
||||
* @param res spatial res represented as a bit shift (MSB is lower res)
|
||||
*/
|
||||
private void relateAndRecurse(final long start, final long end, final short res) {
|
||||
final double minLon = GeoPointField.decodeLongitude(start);
|
||||
final double minLat = GeoPointField.decodeLatitude(start);
|
||||
final double maxLon = GeoPointField.decodeLongitude(end);
|
||||
final double maxLat = GeoPointField.decodeLatitude(end);
|
||||
|
||||
final short level = (short)((BITS<<1)-res>>>1);
|
||||
|
||||
// if cell is within and a factor of the precision step, or it crosses the edge of the shape add the range
|
||||
final boolean within = res % GeoPointField.PRECISION_STEP == 0 && relationImpl.cellWithin(minLat, maxLat, minLon, maxLon);
|
||||
if (within || (level == DETAIL_LEVEL && relationImpl.cellIntersectsShape(minLat, maxLat, minLon, maxLon))) {
|
||||
final short nextRes = (short)(res-1);
|
||||
if (nextRes % GeoPointField.PRECISION_STEP == 0) {
|
||||
rangeBounds.add(new Range(start, nextRes, !within));
|
||||
rangeBounds.add(new Range(start|(1L<<nextRes), nextRes, !within));
|
||||
} else {
|
||||
rangeBounds.add(new Range(start, res, !within));
|
||||
}
|
||||
} else if (level < DETAIL_LEVEL && relationImpl.cellIntersectsMBR(minLat, maxLat, minLon, maxLon)) {
|
||||
computeRange(start, (short) (res - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final BytesRef peek() {
|
||||
rangeBounds.get(0).fillBytesRef(this.nextSubRangeBRB);
|
||||
return nextSubRangeBRB.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void nextRange() {
|
||||
currentRange = rangeBounds.remove(0);
|
||||
super.nextRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final BytesRef nextSeekTerm(BytesRef term) {
|
||||
while (hasNext()) {
|
||||
if (currentRange == null) {
|
||||
nextRange();
|
||||
}
|
||||
// if the new upper bound is before the term parameter, the sub-range is never a hit
|
||||
if (term != null && term.compareTo(currentCell) > 0) {
|
||||
nextRange();
|
||||
if (!rangeBounds.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// never seek backwards, so use current term if lower bound is smaller
|
||||
return (term != null && term.compareTo(currentCell) > 0) ? term : currentCell;
|
||||
}
|
||||
|
||||
// no more sub-range enums available
|
||||
assert rangeBounds.isEmpty();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean hasNext() {
|
||||
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,235 +0,0 @@
|
|||
package org.apache.lucene.spatial.geopoint.search;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.index.TermsEnum;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
|
||||
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.getPrefixCodedShift;
|
||||
|
||||
/**
|
||||
* Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
|
||||
* {@link org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding#PREFIX} method defined by
|
||||
* {@link GeoPointField}. The terms are then enumerated by the
|
||||
* {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or pass
|
||||
* the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the
|
||||
* resulting DocIdSet.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
final class GeoPointPrefixTermsEnum extends GeoPointTermsEnum {
|
||||
private final long start;
|
||||
|
||||
private short shift;
|
||||
|
||||
// current range as long
|
||||
private long currStart;
|
||||
private long currEnd;
|
||||
|
||||
private final Range nextRange = new Range(-1, shift, true);
|
||||
|
||||
private boolean hasNext = false;
|
||||
|
||||
private boolean withinOnly = false;
|
||||
private long lastWithin;
|
||||
|
||||
public GeoPointPrefixTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
|
||||
super(tenum, query);
|
||||
this.start = GeoPointField.encodeLatLon(query.minLat, query.minLon);
|
||||
this.currentRange = new Range(0, shift, true);
|
||||
// start shift at maxShift value (from computeMaxShift)
|
||||
this.shift = maxShift;
|
||||
final long mask = (1L << shift) - 1;
|
||||
this.currStart = start & ~mask;
|
||||
this.currEnd = currStart | mask;
|
||||
}
|
||||
|
||||
private boolean within(final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
return relationImpl.cellWithin(minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
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 {
|
||||
maxLon = GeoPointField.decodeLongitude(currEnd);
|
||||
maxLat = GeoPointField.decodeLatitude(currEnd);
|
||||
|
||||
isWithin = false;
|
||||
// within or a boundary
|
||||
if (boundary(minLat, maxLat, minLon, maxLon) == true) {
|
||||
isWithin = within(minLat, maxLat, minLon, maxLon);
|
||||
final int m;
|
||||
if (isWithin == false || (m = shift % GeoPointField.PRECISION_STEP) == 0) {
|
||||
setNextRange(isWithin == false);
|
||||
advanceVariables();
|
||||
break;
|
||||
} 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
|
||||
if (isWithin == true || (relationImpl.cellIntersectsMBR(minLat, maxLat, minLon, maxLon) == true && shift != maxShift)) {
|
||||
// descend: currStart need not change since shift handles end of range
|
||||
currEnd = currStart | (1L<<--shift) - 1;
|
||||
} else {
|
||||
advanceVariables();
|
||||
minLon = GeoPointField.decodeLongitude(currStart);
|
||||
minLat = GeoPointField.decodeLatitude(currStart);
|
||||
}
|
||||
} while(shift < 63);
|
||||
}
|
||||
|
||||
private void setNextRange(final boolean boundary) {
|
||||
nextRange.start = currStart;
|
||||
nextRange.shift = shift;
|
||||
nextRange.boundary = boundary;
|
||||
}
|
||||
|
||||
private void advanceVariables() {
|
||||
/** set next variables */
|
||||
long shiftMask = 1L << shift;
|
||||
// pop-up if shift bit is set
|
||||
while ( (currStart & shiftMask) == shiftMask) {
|
||||
shiftMask = 1L << ++shift;
|
||||
}
|
||||
final long shiftMOne = shiftMask - 1;
|
||||
currStart = currStart & ~shiftMOne | shiftMask;
|
||||
currEnd = currStart | shiftMOne;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final BytesRef peek() {
|
||||
nextRange.fillBytesRef(nextSubRangeBRB);
|
||||
return super.peek();
|
||||
}
|
||||
|
||||
protected void seek(long term, short res) {
|
||||
if (term < currStart && res < maxShift) {
|
||||
throw new IllegalArgumentException("trying to seek backwards");
|
||||
} else if (term == currStart) {
|
||||
return;
|
||||
}
|
||||
shift = res;
|
||||
currStart = term;
|
||||
currEnd = currStart | ((1L<<shift)-1);
|
||||
withinOnly = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void nextRange() {
|
||||
hasNext = false;
|
||||
super.nextRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean hasNext() {
|
||||
if (hasNext == true || nextWithin()) {
|
||||
return true;
|
||||
}
|
||||
nextRelation();
|
||||
if (currentRange.compareTo(nextRange) != 0) {
|
||||
currentRange.set(nextRange);
|
||||
return (hasNext = true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final BytesRef nextSeekTerm(BytesRef term) {
|
||||
while (hasNext()) {
|
||||
nextRange();
|
||||
if (term == null) {
|
||||
return currentCell;
|
||||
}
|
||||
|
||||
final int comparison = term.compareTo(currentCell);
|
||||
if (comparison > 0) {
|
||||
seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
|
||||
continue;
|
||||
}
|
||||
return currentCell;
|
||||
}
|
||||
|
||||
// no more sub-range enums available
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AcceptStatus accept(BytesRef term) {
|
||||
// range < term or range is null
|
||||
while (currentCell == null || term.compareTo(currentCell) > 0) {
|
||||
// no more ranges, be gone
|
||||
if (hasNext() == false) {
|
||||
return AcceptStatus.END;
|
||||
}
|
||||
|
||||
// peek next range, if the range > term then seek
|
||||
final int peekCompare = term.compareTo(peek());
|
||||
if (peekCompare < 0) {
|
||||
return AcceptStatus.NO_AND_SEEK;
|
||||
} else if (peekCompare > 0) {
|
||||
seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
|
||||
}
|
||||
nextRange();
|
||||
}
|
||||
return AcceptStatus.YES;
|
||||
}
|
||||
|
||||
protected final class Range extends BaseRange {
|
||||
public Range(final long start, final short res, final boolean boundary) {
|
||||
super(start, res, boundary);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillBytesRef(BytesRefBuilder result) {
|
||||
assert result != null;
|
||||
geoCodedToPrefixCoded(start, shift, result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
package org.apache.lucene.spatial.geopoint.search;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -14,64 +16,189 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.geopoint.search;
|
||||
|
||||
import org.apache.lucene.index.FilteredTermsEnum;
|
||||
import org.apache.lucene.index.TermsEnum;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.geopoint.search.GeoPointMultiTermQuery.CellComparator;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
|
||||
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.getPrefixCodedShift;
|
||||
|
||||
/**
|
||||
* Base class for {@link GeoPointNumericTermsEnum} and {@link GeoPointPrefixTermsEnum} which compares
|
||||
* candidate GeoPointField encoded terms against terms matching the defined query criteria.
|
||||
* Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria. The terms
|
||||
* are then enumerated by the {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match
|
||||
* the prefix terms or pass the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the
|
||||
* resulting DocIdSet.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
||||
protected final short maxShift;
|
||||
final class GeoPointTermsEnum extends FilteredTermsEnum {
|
||||
private final short maxShift;
|
||||
private final BytesRefBuilder currentCellBRB = new BytesRefBuilder();
|
||||
private final BytesRefBuilder nextSubRangeBRB = new BytesRefBuilder();
|
||||
private final GeoPointMultiTermQuery.CellComparator relationImpl;
|
||||
|
||||
protected BaseRange currentRange;
|
||||
protected BytesRef currentCell;
|
||||
protected final BytesRefBuilder currentCellBRB = new BytesRefBuilder();
|
||||
protected final BytesRefBuilder nextSubRangeBRB = new BytesRefBuilder();
|
||||
private short shift; // shift mask
|
||||
private long currStart; // range start as encoded long
|
||||
private long currEnd; // range end as encoded long
|
||||
|
||||
protected final CellComparator relationImpl;
|
||||
private final Range currentRange = new Range(-1, shift, true);;
|
||||
private final Range nextRange = new Range(-1, shift, true);
|
||||
private BytesRef currentCell;
|
||||
|
||||
GeoPointTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
|
||||
private boolean hasNext = false;
|
||||
private boolean withinOnly = false;
|
||||
private long lastWithin;
|
||||
|
||||
public GeoPointTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
|
||||
super(tenum);
|
||||
this.maxShift = query.maxShift;
|
||||
this.relationImpl = query.cellComparator;
|
||||
// start shift at maxShift value (from computeMaxShift)
|
||||
this.shift = maxShift;
|
||||
final long mask = (1L << shift) - 1;
|
||||
this.currStart = GeoPointField.encodeLatLon(query.minLat, query.minLon) & ~mask;
|
||||
this.currEnd = currStart | mask;
|
||||
}
|
||||
|
||||
static GeoPointTermsEnum newInstance(final TermsEnum terms, final GeoPointMultiTermQuery query) {
|
||||
if (query.termEncoding == TermEncoding.PREFIX) {
|
||||
return new GeoPointPrefixTermsEnum(terms, query);
|
||||
} else if (query.termEncoding == TermEncoding.NUMERIC) {
|
||||
return new GeoPointNumericTermsEnum(terms, query);
|
||||
private boolean within(final double minLat, final double maxLat, final double minLon, final double maxLon) {
|
||||
return relationImpl.cellWithin(minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid GeoPoint TermEncoding " + query.termEncoding);
|
||||
currStart += (1L << shift);
|
||||
setNextRange(false);
|
||||
currentRange.set(nextRange);
|
||||
hasNext = true;
|
||||
|
||||
withinOnly = lastWithin != currStart;
|
||||
if (withinOnly == false) advanceVariables();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean boundaryTerm() {
|
||||
if (currentCell == null) {
|
||||
throw new IllegalStateException("GeoPointTermsEnum empty or not initialized");
|
||||
private void nextRelation() {
|
||||
double minLon = GeoPointField.decodeLongitude(currStart);
|
||||
double minLat = GeoPointField.decodeLatitude(currStart);
|
||||
double maxLon;
|
||||
double maxLat;
|
||||
boolean isWithin;
|
||||
do {
|
||||
maxLon = GeoPointField.decodeLongitude(currEnd);
|
||||
maxLat = GeoPointField.decodeLatitude(currEnd);
|
||||
|
||||
isWithin = false;
|
||||
// within or a boundary
|
||||
if (boundary(minLat, maxLat, minLon, maxLon) == true) {
|
||||
isWithin = within(minLat, maxLat, minLon, maxLon);
|
||||
final int m;
|
||||
if (isWithin == false || (m = shift % GeoPointField.PRECISION_STEP) == 0) {
|
||||
setNextRange(isWithin == false);
|
||||
advanceVariables();
|
||||
break;
|
||||
} 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
|
||||
if (isWithin == true || (relationImpl.cellIntersectsMBR(minLat, maxLat, minLon, maxLon) == true && shift != maxShift)) {
|
||||
// descend: currStart need not change since shift handles end of range
|
||||
currEnd = currStart | (1L<<--shift) - 1;
|
||||
} else {
|
||||
advanceVariables();
|
||||
minLon = GeoPointField.decodeLongitude(currStart);
|
||||
minLat = GeoPointField.decodeLatitude(currStart);
|
||||
}
|
||||
} while(shift < 63);
|
||||
}
|
||||
|
||||
private void setNextRange(final boolean boundary) {
|
||||
nextRange.start = currStart;
|
||||
nextRange.shift = shift;
|
||||
nextRange.boundary = boundary;
|
||||
}
|
||||
|
||||
private void advanceVariables() {
|
||||
/** set next variables */
|
||||
long shiftMask = 1L << shift;
|
||||
// pop-up if shift bit is set
|
||||
while ( (currStart & shiftMask) == shiftMask) {
|
||||
shiftMask = 1L << ++shift;
|
||||
}
|
||||
return currentRange.boundary;
|
||||
final long shiftMOne = shiftMask - 1;
|
||||
currStart = currStart & ~shiftMOne | shiftMask;
|
||||
currEnd = currStart | shiftMOne;
|
||||
}
|
||||
|
||||
protected BytesRef peek() {
|
||||
protected final BytesRef peek() {
|
||||
nextRange.fillBytesRef(nextSubRangeBRB);
|
||||
return nextSubRangeBRB.get();
|
||||
}
|
||||
|
||||
abstract protected boolean hasNext();
|
||||
protected void seek(long term, short res) {
|
||||
if (term < currStart && res < maxShift) {
|
||||
throw new IllegalArgumentException("trying to seek backwards");
|
||||
} else if (term == currStart) {
|
||||
return;
|
||||
}
|
||||
shift = res;
|
||||
currStart = term;
|
||||
currEnd = currStart | ((1L<<shift)-1);
|
||||
withinOnly = false;
|
||||
}
|
||||
|
||||
protected void nextRange() {
|
||||
hasNext = false;
|
||||
currentRange.fillBytesRef(currentCellBRB);
|
||||
currentCell = currentCellBRB.get();
|
||||
}
|
||||
|
||||
protected final boolean hasNext() {
|
||||
if (hasNext == true || nextWithin()) {
|
||||
return true;
|
||||
}
|
||||
nextRelation();
|
||||
if (currentRange.compareTo(nextRange) != 0) {
|
||||
currentRange.set(nextRange);
|
||||
return (hasNext = true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final BytesRef nextSeekTerm(BytesRef term) {
|
||||
while (hasNext()) {
|
||||
nextRange();
|
||||
if (term == null) {
|
||||
return currentCell;
|
||||
}
|
||||
|
||||
final int comparison = term.compareTo(currentCell);
|
||||
if (comparison > 0) {
|
||||
seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
|
||||
continue;
|
||||
}
|
||||
return currentCell;
|
||||
}
|
||||
|
||||
// no more sub-range enums available
|
||||
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
|
||||
|
@ -82,48 +209,60 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
|||
*/
|
||||
@Override
|
||||
protected AcceptStatus accept(BytesRef term) {
|
||||
// validate value is in range
|
||||
// range < term or range is null
|
||||
while (currentCell == null || term.compareTo(currentCell) > 0) {
|
||||
// no more ranges, be gone
|
||||
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) {
|
||||
|
||||
// peek next range, if the range > term then seek
|
||||
final int peekCompare = term.compareTo(peek());
|
||||
if (peekCompare < 0) {
|
||||
return AcceptStatus.NO_AND_SEEK;
|
||||
} else if (peekCompare > 0) {
|
||||
seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
|
||||
}
|
||||
// step forward to next range without seeking, as next range is less or equal current term
|
||||
nextRange();
|
||||
}
|
||||
|
||||
return AcceptStatus.YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current range term is a boundary of the query shape
|
||||
*/
|
||||
public boolean boundaryTerm() {
|
||||
if (currentCell == null) {
|
||||
throw new IllegalStateException("GeoPointTermsEnum empty or not initialized");
|
||||
}
|
||||
return currentRange.boundary;
|
||||
}
|
||||
|
||||
protected boolean postFilter(final double lat, final double lon) {
|
||||
return relationImpl.postFilter(lat, lon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class to represent a range along the space filling curve
|
||||
*/
|
||||
abstract class BaseRange implements Comparable<BaseRange> {
|
||||
protected short shift;
|
||||
protected long start;
|
||||
protected boolean boundary;
|
||||
protected final class Range implements Comparable<Range> {
|
||||
private short shift;
|
||||
private long start;
|
||||
private boolean boundary;
|
||||
|
||||
BaseRange(final long lower, final short shift, boolean boundary) {
|
||||
public Range(final long start, final short shift, final boolean boundary) {
|
||||
this.boundary = boundary;
|
||||
this.start = lower;
|
||||
this.shift = shift;
|
||||
}
|
||||
this.start = start;
|
||||
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);
|
||||
protected void fillBytesRef(BytesRefBuilder result) {
|
||||
assert result != null;
|
||||
geoCodedToPrefixCoded(start, shift, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(BaseRange other) {
|
||||
public int compareTo(Range other) {
|
||||
final int result = Short.compare(this.shift, other.shift);
|
||||
if (result == 0) {
|
||||
return Long.compare(this.start, other.start);
|
||||
|
@ -131,7 +270,7 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected void set(BaseRange other) {
|
||||
protected void set(Range other) {
|
||||
this.start = other.start;
|
||||
this.shift = other.shift;
|
||||
this.boundary = other.boundary;
|
||||
|
|
|
@ -22,7 +22,6 @@ import org.apache.lucene.geo.BaseGeoPointTestCase;
|
|||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
|
||||
/**
|
||||
* random testing for GeoPoint query logic
|
||||
|
@ -43,22 +42,22 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
|
|||
|
||||
@Override
|
||||
protected void addPointToDoc(String field, Document doc, double lat, double lon) {
|
||||
doc.add(new GeoPointField(field, lat, lon, GeoPointField.PREFIX_TYPE_NOT_STORED));
|
||||
doc.add(new GeoPointField(field, lat, lon, GeoPointField.TYPE_NOT_STORED));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query newRectQuery(String field, double minLat, double maxLat, double minLon, double maxLon) {
|
||||
return new GeoPointInBBoxQuery(field, TermEncoding.PREFIX, minLat, maxLat, minLon, maxLon);
|
||||
return new GeoPointInBBoxQuery(field, minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query newDistanceQuery(String field, double centerLat, double centerLon, double radiusMeters) {
|
||||
return new GeoPointDistanceQuery(field, TermEncoding.PREFIX, centerLat, centerLon, radiusMeters);
|
||||
return new GeoPointDistanceQuery(field, centerLat, centerLon, radiusMeters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query newPolygonQuery(String field, Polygon... polygons) {
|
||||
return new GeoPointInPolygonQuery(field, TermEncoding.PREFIX, polygons);
|
||||
return new GeoPointInPolygonQuery(field, polygons);
|
||||
}
|
||||
|
||||
// TODO: remove these once we get tests passing!
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.geopoint.search;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.geo.BaseGeoPointTestCase;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
|
||||
|
||||
/**
|
||||
* random testing for GeoPoint query logic (with deprecated numeric encoding)
|
||||
* @deprecated remove this when TermEncoding.NUMERIC is removed
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressCodecs("Direct") // can easily create too many postings and blow direct sky high
|
||||
public class TestLegacyGeoPointQuery extends BaseGeoPointTestCase {
|
||||
|
||||
@Override
|
||||
protected double quantizeLat(double lat) {
|
||||
return GeoPointField.decodeLatitude(GeoPointField.encodeLatLon(lat, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double quantizeLon(double lon) {
|
||||
return GeoPointField.decodeLongitude(GeoPointField.encodeLatLon(0, lon));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPointToDoc(String field, Document doc, double lat, double lon) {
|
||||
doc.add(new GeoPointField(field, lat, lon, GeoPointField.NUMERIC_TYPE_NOT_STORED));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query newRectQuery(String field, double minLat, double maxLat, double minLon, double maxLon) {
|
||||
return new GeoPointInBBoxQuery(field, TermEncoding.NUMERIC, minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query newDistanceQuery(String field, double centerLat, double centerLon, double radiusMeters) {
|
||||
return new GeoPointDistanceQuery(field, TermEncoding.NUMERIC, centerLat, centerLon, radiusMeters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query newPolygonQuery(String field, Polygon... polygons) {
|
||||
return new GeoPointInPolygonQuery(field, TermEncoding.NUMERIC, polygons);
|
||||
}
|
||||
|
||||
// legacy encoding is too slow somehow for this random test, its not up to the task.
|
||||
@Override
|
||||
public void testRandomDistance() throws Exception {
|
||||
assumeTrue("legacy encoding is too slow/hangs on this test", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testRandomDistanceHuge() throws Exception {
|
||||
assumeTrue("legacy encoding is too slow/hangs on this test", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testSamePointManyTimes() throws Exception {
|
||||
assumeTrue("legacy encoding goes OOM on this test", false);
|
||||
}
|
||||
|
||||
// TODO: remove these once we get tests passing!
|
||||
|
||||
@Override
|
||||
protected double nextLongitude() {
|
||||
return GeoPointTestUtil.nextLongitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double nextLatitude() {
|
||||
return GeoPointTestUtil.nextLatitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Rectangle nextBox() {
|
||||
return GeoPointTestUtil.nextBox();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Polygon nextPolygon() {
|
||||
return GeoPointTestUtil.nextPolygon();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue