Merge pull request #13672 from MaineC/feature/geo-coerce-consolidation

Switch geo validation to enum
This commit is contained in:
Isabel Drost-Fromm 2015-09-24 22:56:31 +02:00
commit 767508f0aa
14 changed files with 241 additions and 236 deletions

View File

@ -22,7 +22,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.Version;
import org.elasticsearch.common.Numbers;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.io.stream.StreamInput;
@ -47,10 +46,6 @@ import java.util.Objects;
public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBoundingBoxQueryBuilder> {
/** Name of the query. */
public static final String NAME = "geo_bbox";
/** Default for geo point coerce (as of this writing false). */
public static final boolean DEFAULT_COERCE = false;
/** Default for skipping geo point validation (as of this writing false). */
public static final boolean DEFAULT_IGNORE_MALFORMED = false;
/** Default type for executing this query (memory as of this writing). */
public static final GeoExecType DEFAULT_TYPE = GeoExecType.MEMORY;
/** Needed for serialization. */
@ -62,10 +57,8 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
private GeoPoint topLeft = new GeoPoint(Double.NaN, Double.NaN);
/** Bottom right corner coordinates of bounding box.*/
private GeoPoint bottomRight = new GeoPoint(Double.NaN, Double.NaN);
/** Whether or not to infer correct coordinates for wrapping bounding boxes.*/
private boolean coerce = DEFAULT_COERCE;
/** Whether or not to skip geo point validation. */
private boolean ignoreMalformed = DEFAULT_IGNORE_MALFORMED;
/** How to deal with incorrect coordinates.*/
private GeoValidationMethod validationMethod = GeoValidationMethod.DEFAULT;
/** How the query should be run. */
private GeoExecType type = DEFAULT_TYPE;
@ -88,7 +81,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
* @param right The right longitude
*/
public GeoBoundingBoxQueryBuilder setCorners(double top, double left, double bottom, double right) {
if (!ignoreMalformed) {
if (GeoValidationMethod.isIgnoreMalformed(validationMethod) == false) {
if (Numbers.isValidDouble(top) == false) {
throw new IllegalArgumentException("top latitude is invalid: " + top);
}
@ -164,39 +157,21 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
return setCornersOGC(GeoPoint.fromGeohash(bottomLeft), GeoPoint.fromGeohash(topRight));
}
/**
* Specify whether or not to try and fix broken/wrapping bounding boxes.
* If set to true, also enables ignoreMalformed thus disabling geo point
* validation altogether.
**/
public GeoBoundingBoxQueryBuilder coerce(boolean coerce) {
if (coerce) {
this.ignoreMalformed = true;
}
this.coerce = coerce;
return this;
}
/** Returns whether or not to try and fix broken/wrapping bounding boxes. */
public boolean coerce() {
return this.coerce;
}
/**
* Specify whether or not to ignore validation errors of bounding boxes.
* Can only be set if coerce set to false, otherwise calling this
* method has no effect.
**/
public GeoBoundingBoxQueryBuilder ignoreMalformed(boolean ignoreMalformed) {
if (coerce == false) {
this.ignoreMalformed = ignoreMalformed;
}
public GeoBoundingBoxQueryBuilder setValidationMethod(GeoValidationMethod method) {
this.validationMethod = method;
return this;
}
/** Returns whether or not to skip bounding box validation. */
public boolean ignoreMalformed() {
return ignoreMalformed;
/**
* Returns geo coordinate validation method to use.
* */
public GeoValidationMethod getValidationMethod() {
return this.validationMethod;
}
/**
@ -230,7 +205,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
QueryValidationException checkLatLon(boolean indexCreatedBeforeV2_0) {
// validation was not available prior to 2.x, so to support bwc percolation queries we only ignore_malformed on 2.x created indexes
if (ignoreMalformed || indexCreatedBeforeV2_0) {
if (GeoValidationMethod.isIgnoreMalformed(validationMethod) == true || indexCreatedBeforeV2_0) {
return null;
}
@ -264,7 +239,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
GeoPoint luceneTopLeft = new GeoPoint(topLeft);
GeoPoint luceneBottomRight = new GeoPoint(bottomRight);
if (coerce) {
if (GeoValidationMethod.isCoerce(validationMethod)) {
// Special case: if the difference between the left and right is 360 and the right is greater than the left, we are asking for
// the complete longitude range so need to set longitude to the complete longditude range
double right = luceneBottomRight.getLon();
@ -313,8 +288,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
builder.array(GeoBoundingBoxQueryParser.TOP_LEFT, topLeft.getLon(), topLeft.getLat());
builder.array(GeoBoundingBoxQueryParser.BOTTOM_RIGHT, bottomRight.getLon(), bottomRight.getLat());
builder.endObject();
builder.field("coerce", coerce);
builder.field("ignore_malformed", ignoreMalformed);
builder.field("validation_method", validationMethod);
builder.field("type", type);
printBoostAndQueryName(builder);
@ -327,14 +301,13 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
return Objects.equals(topLeft, other.topLeft) &&
Objects.equals(bottomRight, other.bottomRight) &&
Objects.equals(type, other.type) &&
Objects.equals(coerce, other.coerce) &&
Objects.equals(ignoreMalformed, other.ignoreMalformed) &&
Objects.equals(validationMethod, other.validationMethod) &&
Objects.equals(fieldName, other.fieldName);
}
@Override
public int doHashCode() {
return Objects.hash(topLeft, bottomRight, type, coerce, ignoreMalformed, fieldName);
return Objects.hash(topLeft, bottomRight, type, validationMethod, fieldName);
}
@Override
@ -344,8 +317,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
geo.topLeft = geo.topLeft.readFrom(in);
geo.bottomRight = geo.bottomRight.readFrom(in);
geo.type = GeoExecType.readTypeFrom(in);
geo.coerce = in.readBoolean();
geo.ignoreMalformed = in.readBoolean();
geo.validationMethod = GeoValidationMethod.readGeoValidationMethodFrom(in);
return geo;
}
@ -355,8 +327,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
topLeft.writeTo(out);
bottomRight.writeTo(out);
type.writeTo(out);
out.writeBoolean(coerce);
out.writeBoolean(ignoreMalformed);
validationMethod.writeTo(out);
}
@Override

View File

@ -80,8 +80,9 @@ public class GeoBoundingBoxQueryParser implements QueryParser<GeoBoundingBoxQuer
String queryName = null;
String currentFieldName = null;
XContentParser.Token token;
boolean coerce = GeoBoundingBoxQueryBuilder.DEFAULT_COERCE;
boolean ignoreMalformed = GeoBoundingBoxQueryBuilder.DEFAULT_IGNORE_MALFORMED;
boolean coerce = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
boolean ignoreMalformed = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
GeoValidationMethod validationMethod = null;
GeoPoint sparse = new GeoPoint();
@ -144,6 +145,8 @@ public class GeoBoundingBoxQueryParser implements QueryParser<GeoBoundingBoxQuer
if (coerce) {
ignoreMalformed = true;
}
} else if ("validation_method".equals(currentFieldName)) {
validationMethod = GeoValidationMethod.fromString(parser.text());
} else if ("type".equals(currentFieldName)) {
type = parser.text();
} else if ("ignore_malformed".equals(currentFieldName)) {
@ -161,8 +164,12 @@ public class GeoBoundingBoxQueryParser implements QueryParser<GeoBoundingBoxQuer
builder.queryName(queryName);
builder.boost(boost);
builder.type(GeoExecType.fromString(type));
builder.coerce(coerce);
builder.ignoreMalformed(ignoreMalformed);
if (validationMethod != null) {
// ignore deprecated coerce/ignoreMalformed settings if validationMethod is set
builder.setValidationMethod(validationMethod);
} else {
builder.setValidationMethod(GeoValidationMethod.infer(coerce, ignoreMalformed));
}
return builder;
}

View File

@ -56,10 +56,6 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
public static final GeoDistance DEFAULT_GEO_DISTANCE = GeoDistance.DEFAULT;
/** Default for optimising query through pre computed bounding box query. */
public static final String DEFAULT_OPTIMIZE_BBOX = "memory";
/** Default for coercing lon/lat values to a standard coordinate system */
public static final boolean DEFAULT_COERCE = false;
/** Default for accepting accept geo points with invalid latitude or longitude */
public static final boolean DEFAULT_IGNORE_MALFORMED = false;
private final String fieldName;
/** Distance from center to cover. */
@ -70,10 +66,8 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
private GeoDistance geoDistance = DEFAULT_GEO_DISTANCE;
/** Whether or not to use a bbox for pre-filtering. TODO change to enum? */
private String optimizeBbox = DEFAULT_OPTIMIZE_BBOX;
/** Whether or not to normalize longitude and latitude values to a standard coordinate system */
private boolean coerce = DEFAULT_COERCE;
/** Whether or not to accept geo points with invalid latitude or longitude */
private boolean ignoreMalformed = DEFAULT_IGNORE_MALFORMED;
/** How strict should geo coordinate validation be? */
private GeoValidationMethod validationMethod = GeoValidationMethod.DEFAULT;
static final GeoDistanceQueryBuilder PROTOTYPE = new GeoDistanceQueryBuilder("_na_");
@ -197,19 +191,14 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
return this.optimizeBbox;
}
public GeoDistanceQueryBuilder coerce(boolean coerce) {
this.coerce = coerce;
if (this.coerce) {
this.ignoreMalformed = true;
}
return this;
/** Set validaton method for geo coordinates. */
public void setValidationMethod(GeoValidationMethod method) {
this.validationMethod = method;
}
public GeoDistanceQueryBuilder ignoreMalformed(boolean ignoreMalformed) {
if (coerce == false) {
this.ignoreMalformed = ignoreMalformed;
}
return this;
/** Returns validation method for geo coordinates. */
public GeoValidationMethod getValidationMethod() {
return this.validationMethod;
}
@Override
@ -219,8 +208,8 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
throw new QueryShardException(shardContext, "couldn't validate latitude/ longitude values", exception);
}
if (coerce) {
GeoUtils.normalizePoint(center, coerce, coerce);
if (GeoValidationMethod.isCoerce(validationMethod)) {
GeoUtils.normalizePoint(center, true, true);
}
double normDistance = geoDistance.normalize(this.distance, DistanceUnit.DEFAULT);
@ -246,23 +235,21 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
builder.field("distance", distance);
builder.field("distance_type", geoDistance.name().toLowerCase(Locale.ROOT));
builder.field("optimize_bbox", optimizeBbox);
builder.field("coerce", coerce);
builder.field("ignore_malformed", ignoreMalformed);
builder.field("validation_method", validationMethod);
printBoostAndQueryName(builder);
builder.endObject();
}
@Override
public int doHashCode() {
return Objects.hash(center, geoDistance, optimizeBbox, distance, coerce, ignoreMalformed);
return Objects.hash(center, geoDistance, optimizeBbox, distance, validationMethod);
}
@Override
public boolean doEquals(GeoDistanceQueryBuilder other) {
return Objects.equals(fieldName, other.fieldName) &&
(distance == other.distance) &&
(coerce == other.coerce) &&
(ignoreMalformed == other.ignoreMalformed) &&
Objects.equals(validationMethod, other.validationMethod) &&
Objects.equals(center, other.center) &&
Objects.equals(optimizeBbox, other.optimizeBbox) &&
Objects.equals(geoDistance, other.geoDistance);
@ -273,8 +260,7 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
String fieldName = in.readString();
GeoDistanceQueryBuilder result = new GeoDistanceQueryBuilder(fieldName);
result.distance = in.readDouble();
result.coerce = in.readBoolean();
result.ignoreMalformed = in.readBoolean();
result.validationMethod = GeoValidationMethod.readGeoValidationMethodFrom(in);
result.center = GeoPoint.readGeoPointFrom(in);
result.optimizeBbox = in.readString();
result.geoDistance = GeoDistance.readGeoDistanceFrom(in);
@ -285,8 +271,7 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
protected void doWriteTo(StreamOutput out) throws IOException {
out.writeString(fieldName);
out.writeDouble(distance);
out.writeBoolean(coerce);
out.writeBoolean(ignoreMalformed);
validationMethod.writeTo(out);
center.writeTo(out);
out.writeString(optimizeBbox);
geoDistance.writeTo(out);
@ -294,7 +279,7 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
private QueryValidationException checkLatLon(boolean indexCreatedBeforeV2_0) {
// validation was not available prior to 2.x, so to support bwc percolation queries we only ignore_malformed on 2.x created indexes
if (ignoreMalformed || indexCreatedBeforeV2_0) {
if (GeoValidationMethod.isIgnoreMalformed(validationMethod) || indexCreatedBeforeV2_0) {
return null;
}

View File

@ -61,8 +61,9 @@ public class GeoDistanceQueryParser implements QueryParser<GeoDistanceQueryBuild
DistanceUnit unit = GeoDistanceQueryBuilder.DEFAULT_DISTANCE_UNIT;
GeoDistance geoDistance = GeoDistanceQueryBuilder.DEFAULT_GEO_DISTANCE;
String optimizeBbox = GeoDistanceQueryBuilder.DEFAULT_OPTIMIZE_BBOX;
boolean coerce = GeoDistanceQueryBuilder.DEFAULT_COERCE;
boolean ignoreMalformed = GeoDistanceQueryBuilder.DEFAULT_IGNORE_MALFORMED;
boolean coerce = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
boolean ignoreMalformed = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
GeoValidationMethod validationMethod = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
@ -75,6 +76,7 @@ public class GeoDistanceQueryParser implements QueryParser<GeoDistanceQueryBuild
} else if (token == XContentParser.Token.START_OBJECT) {
// the json in the format of -> field : { lat : 30, lon : 12 }
String currentName = parser.currentName();
assert currentFieldName != null;
fieldName = currentFieldName;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
@ -125,6 +127,8 @@ public class GeoDistanceQueryParser implements QueryParser<GeoDistanceQueryBuild
}
} else if ("ignore_malformed".equals(currentFieldName)) {
ignoreMalformed = parser.booleanValue();
} else if ("validation_method".equals(currentFieldName)) {
validationMethod = GeoValidationMethod.fromString(parser.text());
} else {
point.resetFromString(parser.text());
fieldName = currentFieldName;
@ -143,8 +147,11 @@ public class GeoDistanceQueryParser implements QueryParser<GeoDistanceQueryBuild
qb.distance((String) vDistance, unit);
}
qb.point(point);
qb.coerce(coerce);
qb.ignoreMalformed(ignoreMalformed);
if (validationMethod != null) {
qb.setValidationMethod(validationMethod);
} else {
qb.setValidationMethod(GeoValidationMethod.infer(coerce, ignoreMalformed));
}
qb.optimizeBbox(optimizeBbox);
qb.geoDistance(geoDistance);
qb.boost(boost);

View File

@ -46,8 +46,6 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
public static final GeoDistance DEFAULT_GEO_DISTANCE = GeoDistance.DEFAULT;
public static final DistanceUnit DEFAULT_UNIT = DistanceUnit.DEFAULT;
public static final String DEFAULT_OPTIMIZE_BBOX = "memory";
public static final boolean DEFAULT_COERCE = false;
public static final boolean DEFAULT_IGNORE_MALFORMED = false;
private final String fieldName;
@ -64,9 +62,7 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
private String optimizeBbox = DEFAULT_OPTIMIZE_BBOX;
private boolean coerce = DEFAULT_COERCE;
private boolean ignoreMalformed = DEFAULT_IGNORE_MALFORMED;
private GeoValidationMethod validationMethod = GeoValidationMethod.DEFAULT;
static final GeoDistanceRangeQueryBuilder PROTOTYPE = new GeoDistanceRangeQueryBuilder("_na_", new GeoPoint());
@ -199,27 +195,15 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
return optimizeBbox;
}
public GeoDistanceRangeQueryBuilder coerce(boolean coerce) {
if (coerce) {
this.ignoreMalformed = true;
}
this.coerce = coerce;
/** Set validation method for coordinates. */
public GeoDistanceRangeQueryBuilder setValidationMethod(GeoValidationMethod method) {
this.validationMethod = method;
return this;
}
public boolean coerce() {
return this.coerce;
}
public GeoDistanceRangeQueryBuilder ignoreMalformed(boolean ignoreMalformed) {
if (coerce == false) {
this.ignoreMalformed = ignoreMalformed;
}
return this;
}
public boolean ignoreMalformed() {
return ignoreMalformed;
/** Returns validation method for coordinates. */
public GeoValidationMethod getValidationMethod(GeoValidationMethod method) {
return this.validationMethod;
}
@Override
@ -228,7 +212,7 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
final boolean indexCreatedBeforeV2_0 = context.indexVersionCreated().before(Version.V_2_0_0);
// validation was not available prior to 2.x, so to support bwc
// percolation queries we only ignore_malformed on 2.x created indexes
if (!indexCreatedBeforeV2_0 && !ignoreMalformed) {
if (!indexCreatedBeforeV2_0 && !GeoValidationMethod.isIgnoreMalformed(validationMethod)) {
if (!GeoUtils.isValidLatitude(point.lat())) {
throw new QueryShardException(context, "illegal latitude value [{}] for [{}]", point.lat(), NAME);
}
@ -237,8 +221,8 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
}
}
if (coerce) {
GeoUtils.normalizePoint(point, coerce, coerce);
if (GeoValidationMethod.isCoerce(validationMethod)) {
GeoUtils.normalizePoint(point, true, true);
}
Double fromValue = null;
@ -285,8 +269,7 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
builder.field(GeoDistanceRangeQueryParser.UNIT_FIELD.getPreferredName(), unit);
builder.field(GeoDistanceRangeQueryParser.DISTANCE_TYPE_FIELD.getPreferredName(), geoDistance.name().toLowerCase(Locale.ROOT));
builder.field(GeoDistanceRangeQueryParser.OPTIMIZE_BBOX_FIELD.getPreferredName(), optimizeBbox);
builder.field(GeoDistanceRangeQueryParser.COERCE_FIELD.getPreferredName(), coerce);
builder.field(GeoDistanceRangeQueryParser.IGNORE_MALFORMED_FIELD.getPreferredName(), ignoreMalformed);
builder.field(GeoDistanceRangeQueryParser.VALIDATION_METHOD.getPreferredName(), validationMethod);
printBoostAndQueryName(builder);
builder.endObject();
}
@ -301,8 +284,7 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
queryBuilder.unit = DistanceUnit.valueOf(in.readString());
queryBuilder.geoDistance = GeoDistance.readGeoDistanceFrom(in);
queryBuilder.optimizeBbox = in.readString();
queryBuilder.coerce = in.readBoolean();
queryBuilder.ignoreMalformed = in.readBoolean();
queryBuilder.validationMethod = GeoValidationMethod.readGeoValidationMethodFrom(in);
return queryBuilder;
}
@ -317,49 +299,25 @@ public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder<GeoDistan
out.writeString(unit.name());
geoDistance.writeTo(out);;
out.writeString(optimizeBbox);
out.writeBoolean(coerce);
out.writeBoolean(ignoreMalformed);
validationMethod.writeTo(out);
}
@Override
protected boolean doEquals(GeoDistanceRangeQueryBuilder other) {
if (!Objects.equals(fieldName, other.fieldName)) {
return false;
}
if (!Objects.equals(point, other.point)) {
return false;
}
if (!Objects.equals(from, other.from)) {
return false;
}
if (!Objects.equals(to, other.to)) {
return false;
}
if (!Objects.equals(includeUpper, other.includeUpper)) {
return false;
}
if (!Objects.equals(includeLower, other.includeLower)) {
return false;
}
if (!Objects.equals(geoDistance, other.geoDistance)) {
return false;
}
if (!Objects.equals(optimizeBbox, other.optimizeBbox)) {
return false;
}
if (!Objects.equals(coerce, other.coerce)) {
return false;
}
if (!Objects.equals(ignoreMalformed, other.ignoreMalformed)) {
return false;
}
return true;
return ((Objects.equals(fieldName, other.fieldName)) &&
(Objects.equals(point, other.point)) &&
(Objects.equals(from, other.from)) &&
(Objects.equals(to, other.to)) &&
(Objects.equals(includeUpper, other.includeUpper)) &&
(Objects.equals(includeLower, other.includeLower)) &&
(Objects.equals(geoDistance, other.geoDistance)) &&
(Objects.equals(optimizeBbox, other.optimizeBbox)) &&
(Objects.equals(validationMethod, other.validationMethod)));
}
@Override
protected int doHashCode() {
return Objects.hash(fieldName, point, from, to, includeUpper, includeLower, geoDistance, optimizeBbox, coerce,
ignoreMalformed);
return Objects.hash(fieldName, point, from, to, includeUpper, includeLower, geoDistance, optimizeBbox, validationMethod);
}
@Override

View File

@ -54,6 +54,7 @@ public class GeoDistanceRangeQueryParser implements QueryParser<GeoDistanceRange
public static final ParseField OPTIMIZE_BBOX_FIELD = new ParseField("optimize_bbox");
public static final ParseField COERCE_FIELD = new ParseField("coerce", "normalize");
public static final ParseField IGNORE_MALFORMED_FIELD = new ParseField("ignore_malformed");
public static final ParseField VALIDATION_METHOD = new ParseField("validation_method");
@Override
public String[] names() {
@ -83,8 +84,10 @@ public class GeoDistanceRangeQueryParser implements QueryParser<GeoDistanceRange
DistanceUnit unit = null;
GeoDistance geoDistance = null;
String optimizeBbox = null;
Boolean coerce = null;
Boolean ignoreMalformed = null;
boolean coerce = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
boolean ignoreMalformed = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
GeoValidationMethod validationMethod = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
@ -183,6 +186,8 @@ public class GeoDistanceRangeQueryParser implements QueryParser<GeoDistanceRange
coerce = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_MALFORMED_FIELD)) {
ignoreMalformed = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, VALIDATION_METHOD)) {
validationMethod = GeoValidationMethod.fromString(parser.text());
} else {
if (point == null) {
point = new GeoPoint();
@ -238,12 +243,11 @@ public class GeoDistanceRangeQueryParser implements QueryParser<GeoDistanceRange
queryBuilder.optimizeBbox(optimizeBbox);
}
if (coerce != null) {
queryBuilder.coerce(coerce);
}
if (ignoreMalformed != null) {
queryBuilder.ignoreMalformed(ignoreMalformed);
if (validationMethod != null) {
// if validation method is set explicitly ignore deprecated coerce/ignore malformed fields if any
queryBuilder.setValidationMethod(validationMethod);
} else {
queryBuilder.setValidationMethod(GeoValidationMethod.infer(coerce, ignoreMalformed));
}
return queryBuilder;
}

View File

@ -51,9 +51,7 @@ public class GeoPolygonQueryBuilder extends AbstractQueryBuilder<GeoPolygonQuery
private final List<GeoPoint> shell;
private boolean coerce = false;
private boolean ignoreMalformed = false;
private GeoValidationMethod validationMethod = GeoValidationMethod.DEFAULT;
public GeoPolygonQueryBuilder(String fieldName, List<GeoPoint> points) {
if (Strings.isEmpty(fieldName)) {
@ -85,27 +83,15 @@ public class GeoPolygonQueryBuilder extends AbstractQueryBuilder<GeoPolygonQuery
return shell;
}
public GeoPolygonQueryBuilder coerce(boolean coerce) {
if (coerce) {
this.ignoreMalformed = true;
}
this.coerce = coerce;
/** Sets the validation method to use for geo coordinates. */
public GeoPolygonQueryBuilder setValidationMethod(GeoValidationMethod method) {
this.validationMethod = method;
return this;
}
public boolean coerce() {
return this.coerce;
}
public GeoPolygonQueryBuilder ignoreMalformed(boolean ignoreMalformed) {
if (coerce == false) {
this.ignoreMalformed = ignoreMalformed;
}
return this;
}
public boolean ignoreMalformed() {
return ignoreMalformed;
/** Returns the validation method to use for geo coordinates. */
public GeoValidationMethod getValidationMethod() {
return this.validationMethod;
}
@Override
@ -118,7 +104,7 @@ public class GeoPolygonQueryBuilder extends AbstractQueryBuilder<GeoPolygonQuery
final boolean indexCreatedBeforeV2_0 = context.indexVersionCreated().before(Version.V_2_0_0);
// validation was not available prior to 2.x, so to support bwc
// percolation queries we only ignore_malformed on 2.x created indexes
if (!indexCreatedBeforeV2_0 && !ignoreMalformed) {
if (!indexCreatedBeforeV2_0 && !GeoValidationMethod.isIgnoreMalformed(validationMethod)) {
for (GeoPoint point : shell) {
if (!GeoUtils.isValidLatitude(point.lat())) {
throw new QueryShardException(context, "illegal latitude value [{}] for [{}]", point.lat(),
@ -131,9 +117,9 @@ public class GeoPolygonQueryBuilder extends AbstractQueryBuilder<GeoPolygonQuery
}
}
if (coerce) {
if (GeoValidationMethod.isCoerce(validationMethod)) {
for (GeoPoint point : shell) {
GeoUtils.normalizePoint(point, coerce, coerce);
GeoUtils.normalizePoint(point, true, true);
}
}
@ -161,8 +147,8 @@ public class GeoPolygonQueryBuilder extends AbstractQueryBuilder<GeoPolygonQuery
builder.endArray();
builder.endObject();
builder.field(GeoPolygonQueryParser.COERCE_FIELD.getPreferredName(), coerce);
builder.field(GeoPolygonQueryParser.IGNORE_MALFORMED_FIELD.getPreferredName(), ignoreMalformed);
builder.field(GeoPolygonQueryParser.COERCE_FIELD.getPreferredName(), GeoValidationMethod.isCoerce(validationMethod));
builder.field(GeoPolygonQueryParser.IGNORE_MALFORMED_FIELD.getPreferredName(), GeoValidationMethod.isIgnoreMalformed(validationMethod));
printBoostAndQueryName(builder);
builder.endObject();
@ -177,8 +163,7 @@ public class GeoPolygonQueryBuilder extends AbstractQueryBuilder<GeoPolygonQuery
shell.add(GeoPoint.readGeoPointFrom(in));
}
GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(fieldName, shell);
builder.coerce = in.readBoolean();
builder.ignoreMalformed = in.readBoolean();
builder.validationMethod = GeoValidationMethod.readGeoValidationMethodFrom(in);
return builder;
}
@ -189,21 +174,19 @@ public class GeoPolygonQueryBuilder extends AbstractQueryBuilder<GeoPolygonQuery
for (GeoPoint point : shell) {
point.writeTo(out);
}
out.writeBoolean(coerce);
out.writeBoolean(ignoreMalformed);
validationMethod.writeTo(out);
}
@Override
protected boolean doEquals(GeoPolygonQueryBuilder other) {
return Objects.equals(coerce, other.coerce)
return Objects.equals(validationMethod, other.validationMethod)
&& Objects.equals(fieldName, other.fieldName)
&& Objects.equals(ignoreMalformed, other.ignoreMalformed)
&& Objects.equals(shell, other.shell);
}
@Override
protected int doHashCode() {
return Objects.hash(coerce, fieldName, ignoreMalformed, shell);
return Objects.hash(validationMethod, fieldName, shell);
}
@Override

View File

@ -46,6 +46,7 @@ public class GeoPolygonQueryParser implements QueryParser<GeoPolygonQueryBuilder
public static final ParseField COERCE_FIELD = new ParseField("coerce", "normalize");
public static final ParseField IGNORE_MALFORMED_FIELD = new ParseField("ignore_malformed");
public static final ParseField VALIDATION_METHOD = new ParseField("validation_method");
public static final ParseField POINTS_FIELD = new ParseField("points");
@Override
@ -62,8 +63,9 @@ public class GeoPolygonQueryParser implements QueryParser<GeoPolygonQueryBuilder
List<GeoPoint> shell = null;
Float boost = null;
Boolean coerce = null;
Boolean ignoreMalformed = null;
boolean coerce = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
boolean ignoreMalformed = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
GeoValidationMethod validationMethod = null;
String queryName = null;
String currentFieldName = null;
XContentParser.Token token;
@ -106,6 +108,8 @@ public class GeoPolygonQueryParser implements QueryParser<GeoPolygonQueryBuilder
}
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_MALFORMED_FIELD)) {
ignoreMalformed = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, VALIDATION_METHOD)) {
validationMethod = GeoValidationMethod.fromString(parser.text());
} else {
throw new ParsingException(parser.getTokenLocation(), "[geo_polygon] query does not support [" + currentFieldName + "]");
}
@ -114,12 +118,13 @@ public class GeoPolygonQueryParser implements QueryParser<GeoPolygonQueryBuilder
}
}
GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(fieldName, shell);
if (coerce != null) {
builder.coerce(coerce);
}
if (ignoreMalformed != null) {
builder.ignoreMalformed(ignoreMalformed);
if (validationMethod != null) {
// if GeoValidationMethod was explicitly set ignore deprecated coerce and ignoreMalformed settings
builder.setValidationMethod(validationMethod);
} else {
builder.setValidationMethod(GeoValidationMethod.infer(coerce, ignoreMalformed));
}
if (queryName != null) {
builder.queryName(queryName);
}

View File

@ -0,0 +1,89 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.index.query;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.util.CollectionUtils;
import java.io.IOException;
/**
* This enum is used to determine how to deal with invalid geo coordinates in geo related
* queries:
*
* On STRICT validation invalid coordinates cause an exception to be thrown.
* On IGNORE_MALFORMED invalid coordinates are being accepted.
* On COERCE invalid coordinates are being corrected to the most likely valid coordinate.
* */
public enum GeoValidationMethod implements Writeable<GeoValidationMethod>{
COERCE, IGNORE_MALFORMED, STRICT;
public static final GeoValidationMethod DEFAULT = STRICT;
public static final boolean DEFAULT_LENIENT_PARSING = (DEFAULT != STRICT);
private static final GeoValidationMethod PROTOTYPE = DEFAULT;
@Override
public GeoValidationMethod readFrom(StreamInput in) throws IOException {
return GeoValidationMethod.values()[in.readVInt()];
}
public static GeoValidationMethod readGeoValidationMethodFrom(StreamInput in) throws IOException {
return PROTOTYPE.readFrom(in);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(this.ordinal());
}
public static GeoValidationMethod fromString(String op) {
for (GeoValidationMethod method : GeoValidationMethod.values()) {
if (method.name().equalsIgnoreCase(op)) {
return method;
}
}
throw new IllegalArgumentException("operator needs to be either " + CollectionUtils.arrayAsArrayList(GeoValidationMethod.values())
+ ", but not [" + op + "]");
}
/** Returns whether or not to skip bounding box validation. */
public static boolean isIgnoreMalformed(GeoValidationMethod method) {
return (method == GeoValidationMethod.IGNORE_MALFORMED || method == GeoValidationMethod.COERCE);
}
/** Returns whether or not to try and fix broken/wrapping bounding boxes. */
public static boolean isCoerce(GeoValidationMethod method) {
return method == GeoValidationMethod.COERCE;
}
/** Returns validation method corresponding to given coerce and ignoreMalformed values. */
public static GeoValidationMethod infer(boolean coerce, boolean ignoreMalformed) {
if (coerce) {
return GeoValidationMethod.COERCE;
} else if (ignoreMalformed) {
return GeoValidationMethod.IGNORE_MALFORMED;
} else {
return GeoValidationMethod.STRICT;
}
}
}

View File

@ -73,10 +73,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
}
if (randomBoolean()) {
builder.coerce(randomBoolean());
}
if (randomBoolean()) {
builder.ignoreMalformed(randomBoolean());
builder.setValidationMethod(randomFrom(GeoValidationMethod.values()));
}
builder.type(randomFrom(GeoExecType.values()));
@ -119,7 +116,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
PointTester[] testers = { new TopTester(), new LeftTester(), new BottomTester(), new RightTester() };
GeoBoundingBoxQueryBuilder builder = createTestQueryBuilder();
builder.coerce(false).ignoreMalformed(false);
builder.setValidationMethod(GeoValidationMethod.STRICT);
for (PointTester tester : testers) {
try {
@ -136,7 +133,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
PointTester[] testers = { new TopTester(), new LeftTester(), new BottomTester(), new RightTester() };
GeoBoundingBoxQueryBuilder builder = createTestQueryBuilder();
builder.ignoreMalformed(true);
builder.setValidationMethod(GeoValidationMethod.IGNORE_MALFORMED);
for (PointTester tester : testers) {
tester.invalidateCoordinate(builder, true);
@ -152,7 +149,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
QueryValidationException except = null;
GeoBoundingBoxQueryBuilder builder = createTestQueryBuilder();
tester.invalidateCoordinate(builder.coerce(true), false);
tester.invalidateCoordinate(builder.setValidationMethod(GeoValidationMethod.COERCE), false);
except = builder.checkLatLon(true);
assertNull("Inner post 2.0 validation w/ coerce should ignore invalid "
+ tester.getClass().getName()
@ -160,7 +157,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
+ tester.invalidCoordinate + " ",
except);
tester.invalidateCoordinate(builder.coerce(true), false);
tester.invalidateCoordinate(builder.setValidationMethod(GeoValidationMethod.COERCE), false);
except = builder.checkLatLon(false);
assertNull("Inner pre 2.0 validation w/ coerce should ignore invalid coordinate: "
+ tester.getClass().getName()
@ -168,7 +165,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
+ tester.invalidCoordinate + " ",
except);
tester.invalidateCoordinate(builder.coerce(false).ignoreMalformed(false), false);
tester.invalidateCoordinate(builder.setValidationMethod(GeoValidationMethod.STRICT), false);
except = builder.checkLatLon(true);
assertNull("Inner pre 2.0 validation w/o coerce should ignore invalid coordinate for old indexes: "
+ tester.getClass().getName()
@ -176,7 +173,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
+ tester.invalidCoordinate,
except);
tester.invalidateCoordinate(builder.coerce(false).ignoreMalformed(false), false);
tester.invalidateCoordinate(builder.setValidationMethod(GeoValidationMethod.STRICT), false);
except = builder.checkLatLon(false);
assertNotNull("Inner post 2.0 validation w/o coerce should detect invalid coordinate: "
+ tester.getClass().getName()
@ -196,7 +193,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
assumeTrue("top should not be equal to bottom for flip check", top != bottom);
System.out.println("top: " + top + " bottom: " + bottom);
builder.coerce(false).ignoreMalformed(false).setCorners(bottom, left, top, right);
builder.setValidationMethod(GeoValidationMethod.STRICT).setCorners(bottom, left, top, right);
}
@Test
@ -208,7 +205,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
double right = builder.bottomRight().getLon();
assumeTrue("top should not be equal to bottom for flip check", top != bottom);
builder.coerce(false).ignoreMalformed(true).setCorners(bottom, left, top, right);
builder.setValidationMethod(GeoValidationMethod.IGNORE_MALFORMED).setCorners(bottom, left, top, right);
}
@Test
@ -219,8 +216,8 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
double bottom = builder.bottomRight().getLat();
double right = builder.bottomRight().getLon();
builder.ignoreMalformed(true).setCorners(top, right, bottom, left);
builder.ignoreMalformed(false).setCorners(top, right, bottom, left);
builder.setValidationMethod(GeoValidationMethod.IGNORE_MALFORMED).setCorners(top, right, bottom, left);
builder.setValidationMethod(GeoValidationMethod.STRICT).setCorners(top, right, bottom, left);
}
@Test
@ -230,7 +227,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
if (getCurrentTypes().length != 0 && "mapped_geo".equals(qb.fieldName())) {
// only execute this test if we are running on a valid geo field
qb.setCorners(200, 200, qb.bottomRight().getLat(), qb.bottomRight().getLon());
qb.coerce(true);
qb.setValidationMethod(GeoValidationMethod.COERCE);
Query query = qb.toQuery(createShardContext());
if (query instanceof ConstantScoreQuery) {
ConstantScoreQuery result = (ConstantScoreQuery) query;
@ -246,6 +243,11 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
}
}
}
@Test
public void checkStrictnessDefault() {
assertFalse("Someone changed the default for coordinate validation - were the docs changed as well?", GeoValidationMethod.DEFAULT_LENIENT_PARSING);
}
@Override
protected void doAssertLuceneQuery(GeoBoundingBoxQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {

View File

@ -59,11 +59,7 @@ public class GeoDistanceQueryBuilderTests extends AbstractQueryTestCase<GeoDista
qb.point(new GeoPoint(p.getY(), p.getX()));
if (randomBoolean()) {
qb.coerce(randomBoolean());
}
if (randomBoolean()) {
qb.ignoreMalformed(randomBoolean());
qb.setValidationMethod(randomFrom(GeoValidationMethod.values()));
}
if (randomBoolean()) {

View File

@ -96,10 +96,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
builder.optimizeBbox(randomFrom("none", "memory", "indexed"));
}
if (randomBoolean()) {
builder.coerce(randomBoolean());
}
if (randomBoolean()) {
builder.ignoreMalformed(randomBoolean());
builder.setValidationMethod(randomFrom(GeoValidationMethod.values()));
}
return builder;
}

View File

@ -49,8 +49,9 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
protected GeoPolygonQueryBuilder doCreateTestQueryBuilder() {
List<GeoPoint> polygon = randomPolygon(randomIntBetween(4, 50));
GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(GEO_POINT_FIELD_NAME, polygon);
builder.coerce(randomBoolean());
builder.ignoreMalformed(randomBoolean());
if (randomBoolean()) {
builder.setValidationMethod(randomFrom(GeoValidationMethod.values()));
}
return builder;
}
@ -62,7 +63,7 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
List<GeoPoint> queryBuilderPoints = queryBuilder.points();
GeoPoint[] queryPoints = geoQuery.points();
assertThat(queryPoints.length, equalTo(queryBuilderPoints.size()));
if (queryBuilder.coerce()) {
if (GeoValidationMethod.isCoerce(queryBuilder.getValidationMethod())) {
for (int i = 0; i < queryBuilderPoints.size(); i++) {
GeoPoint queryBuilderPoint = queryBuilderPoints.get(i);
GeoUtils.normalizePoint(queryBuilderPoint, true, true);

View File

@ -22,12 +22,12 @@ package org.elasticsearch.search.geo;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.GeoValidationMethod;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.test.ESIntegTestCase;
import org.junit.Test;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
@ -289,43 +289,43 @@ public class GeoBoundingBoxIT extends ESIntegTestCase {
SearchResponse searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(50, -180, -50, 180)
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180)
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(50, -180, -50, 180).type("indexed")
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180).type("indexed")
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(90, -180, -90, 180)
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180)
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(90, -180, -90, 180).type("indexed")
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180).type("indexed")
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(50, 0, -50, 360)
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360)
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(50, 0, -50, 360).type("indexed")
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360).type("indexed")
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(90, 0, -90, 360)
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360)
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
searchResponse = client().prepareSearch()
.setQuery(
geoBoundingBoxQuery("location").coerce(true).setCorners(90, 0, -90, 360).type("indexed")
geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360).type("indexed")
).execute().actionGet();
assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
}