From 1d48057b2b7c9bab9e4ee787b40f8eb2e8fca900 Mon Sep 17 00:00:00 2001 From: shubhangi2901 Date: Tue, 26 May 2020 10:45:37 -0700 Subject: [PATCH 1/7] adding abstract geofieldtype in bounding box queries --- .../common/geo/GeoBoundingBox.java | 21 ++++++++++++------- .../common/io/stream/StreamInput.java | 6 ++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java index 01315d3e9848c..c6ae9fd97696c 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java @@ -31,6 +31,7 @@ import org.elasticsearch.geometry.ShapeType; import org.elasticsearch.geometry.utils.StandardValidator; import org.elasticsearch.geometry.utils.WellKnownText; +import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper.AbstractGeometryFieldType; import java.io.IOException; import java.text.ParseException; @@ -54,17 +55,18 @@ public class GeoBoundingBox implements ToXContentObject, Writeable { public static final ParseField LON_FIELD = new ParseField("lon"); public static final ParseField TOP_LEFT_FIELD = new ParseField("top_left"); public static final ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right"); + private AbstractGeometryFieldType abstractGeometryFieldType; - private final GeoPoint topLeft; - private final GeoPoint bottomRight; - - public GeoBoundingBox(GeoPoint topLeft, GeoPoint bottomRight) { - this.topLeft = topLeft; - this.bottomRight = bottomRight; - } +// private final GeoPoint topLeft; +// private final GeoPoint bottomRight; +// +// public GeoBoundingBox(GeoPoint topLeft, GeoPoint bottomRight) { +// this.topLeft = topLeft; +// this.bottomRight = bottomRight; +// } public GeoBoundingBox(StreamInput input) throws IOException { - this.topLeft = input.readGeoPoint(); + this.topLeft = input.reada; this.bottomRight = input.readGeoPoint(); } @@ -73,6 +75,9 @@ public boolean isUnbounded() { || Double.isNaN(bottomRight.lon()) || Double.isNaN(bottomRight.lat()); } + public GeoBoundingBox(AbstractGeometryFieldType abstractGeometryFieldType){ + this.abstractGeometryFieldType = abstractGeometryFieldType; + } public GeoPoint topLeft() { return topLeft; } diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index 7de344d95dc46..7b57984cc0f4d 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -41,6 +41,7 @@ import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; +import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper; import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.joda.time.DateTimeZone; @@ -735,6 +736,8 @@ public Object readGenericValue() throws IOException { return readGeoPoint(); case 23: return readZonedDateTime(); + case 24: + return readAbstractGeomteryType(); default: throw new IOException("Can't read unknown type [" + type + "]"); } @@ -831,6 +834,9 @@ public GeoPoint readGeoPoint() throws IOException { return new GeoPoint(readDouble(), readDouble()); } + public AbstractGeometryFieldMapper.AbstractGeometryFieldType readAbstractGeomteryType() throws Exception{ + return AbstractGeometryFieldMapper.AbstractGeometryFieldType(); + } /** * Read a {@linkplain DateTimeZone}. */ From f793983d92a77d33d39eb22a2b7d3bdc532d7654 Mon Sep 17 00:00:00 2001 From: shubhangi2901 Date: Mon, 15 Jun 2020 12:17:52 -0700 Subject: [PATCH 2/7] add validation queries for GeoBoundingBox Query Builder --- .../common/geo/GeoBoundingBox.java | 21 +++++++-------- .../common/io/stream/StreamInput.java | 8 +++--- .../query/GeoBoundingBoxQueryBuilder.java | 26 ++++++++++++++++--- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java index c6ae9fd97696c..fbbda41a27a39 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java @@ -55,18 +55,18 @@ public class GeoBoundingBox implements ToXContentObject, Writeable { public static final ParseField LON_FIELD = new ParseField("lon"); public static final ParseField TOP_LEFT_FIELD = new ParseField("top_left"); public static final ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right"); - private AbstractGeometryFieldType abstractGeometryFieldType; +// private AbstractGeometryFieldType abstractGeometryFieldType; -// private final GeoPoint topLeft; -// private final GeoPoint bottomRight; -// -// public GeoBoundingBox(GeoPoint topLeft, GeoPoint bottomRight) { -// this.topLeft = topLeft; -// this.bottomRight = bottomRight; -// } + private final GeoPoint topLeft; + private final GeoPoint bottomRight; + + public GeoBoundingBox(GeoPoint topLeft, GeoPoint bottomRight) { + this.topLeft = topLeft; + this.bottomRight = bottomRight; + } public GeoBoundingBox(StreamInput input) throws IOException { - this.topLeft = input.reada; + this.topLeft = input.readGeoPoint(); this.bottomRight = input.readGeoPoint(); } @@ -75,9 +75,6 @@ public boolean isUnbounded() { || Double.isNaN(bottomRight.lon()) || Double.isNaN(bottomRight.lat()); } - public GeoBoundingBox(AbstractGeometryFieldType abstractGeometryFieldType){ - this.abstractGeometryFieldType = abstractGeometryFieldType; - } public GeoPoint topLeft() { return topLeft; } diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index 7b57984cc0f4d..9823a4ade9ac9 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -736,8 +736,6 @@ public Object readGenericValue() throws IOException { return readGeoPoint(); case 23: return readZonedDateTime(); - case 24: - return readAbstractGeomteryType(); default: throw new IOException("Can't read unknown type [" + type + "]"); } @@ -834,9 +832,9 @@ public GeoPoint readGeoPoint() throws IOException { return new GeoPoint(readDouble(), readDouble()); } - public AbstractGeometryFieldMapper.AbstractGeometryFieldType readAbstractGeomteryType() throws Exception{ - return AbstractGeometryFieldMapper.AbstractGeometryFieldType(); - } +// public AbstractGeometryFieldMapper.AbstractGeometryFieldType readAbstractGeomteryType() throws Exception{ +// return AbstractGeometryFieldMapper.AbstractGeometryFieldType(); +// } /** * Read a {@linkplain DateTimeZone}. */ diff --git a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java index 59025dc03e161..d30802db5dd58 100644 --- a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java @@ -37,10 +37,15 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.geometry.Rectangle; import org.elasticsearch.geometry.utils.Geohash; +import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.GeoPointFieldMapper.GeoPointFieldType; +import org.elasticsearch.index.mapper.GeoShapeFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -76,6 +81,16 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder validContentTypes = + Collections.unmodifiableList( + Arrays.asList( + GeoShapeFieldMapper.CONTENT_TYPE, + GeoPointFieldMapper.CONTENT_TYPE)); + + protected List validContentTypes() { + return validContentTypes; + } + /** * Create new bounding box query. * @param fieldName name of index field containing geo coordinates to operate on. @@ -312,10 +327,15 @@ public Query doToQuery(QueryShardContext context) { throw new QueryShardException(context, "failed to find geo_point field [" + fieldName + "]"); } } - if (!(fieldType instanceof GeoPointFieldType)) { - throw new QueryShardException(context, "field [" + fieldName + "] is not a geo_point field"); +// if (!(fieldType instanceof GeoPointFieldType)) { +// throw new QueryShardException(context, "field [" + fieldName + "] is not a geo_point field"); +// } + if (!validContentTypes().contains(fieldType.typeName())) { + throw new QueryShardException(context, + "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. [" + + NAME + "] query supports the following types [" + + String.join(",", validContentTypes()) + "]"); } - QueryValidationException exception = checkLatLon(); if (exception != null) { throw new QueryShardException(context, "couldn't validate latitude/ longitude values", exception); From 1ca44be4ee36aa4d85c0edc055aa61acb08ba86a Mon Sep 17 00:00:00 2001 From: shubhangi2901 Date: Mon, 15 Jun 2020 15:52:48 -0700 Subject: [PATCH 3/7] add validation of abstractgeometryfieldtype in geo distance --- .../index/query/GeoDistanceQueryBuilder.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java index 5db7516437314..0e69731091f4e 100644 --- a/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java @@ -35,12 +35,13 @@ import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.GeoPointFieldMapper.GeoPointFieldType; +import org.elasticsearch.index.mapper.GeoShapeFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import java.io.IOException; -import java.util.Locale; -import java.util.Objects; +import java.util.*; /** * Filter results of a query to include only those within a specific distance to some @@ -77,6 +78,15 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder validContentTypes = + Collections.unmodifiableList( + Arrays.asList( + GeoShapeFieldMapper.CONTENT_TYPE, + GeoPointFieldMapper.CONTENT_TYPE)); + + protected List validContentTypes() { + return validContentTypes; + } /** * Construct new GeoDistanceQueryBuilder. * @param fieldName name of indexed geo field to operate distance computation on. @@ -236,8 +246,15 @@ protected Query doToQuery(QueryShardContext shardContext) throws IOException { } } - if (!(fieldType instanceof GeoPointFieldType)) { - throw new QueryShardException(shardContext, "field [" + fieldName + "] is not a geo_point field"); +// if (!(fieldType instanceof GeoPointFieldType)) { +// throw new QueryShardException(shardContext, "field [" + fieldName + "] is not a geo_point field"); +// } + + if (!validContentTypes().contains(fieldType.typeName())) { + throw new QueryShardException(shardContext, + "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. [" + + NAME + "] query supports the following types [" + + String.join(",", validContentTypes()) + "]"); } QueryValidationException exception = checkLatLon(); From 8e26cf15dd51c6aedf15bb93279561a4df954a0c Mon Sep 17 00:00:00 2001 From: Shubhangi Prasad Date: Mon, 15 Jun 2020 16:04:19 -0700 Subject: [PATCH 4/7] Update GeoBoundingBox.java --- .../main/java/org/elasticsearch/common/geo/GeoBoundingBox.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java index fbbda41a27a39..aee25500b0fb0 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java @@ -31,7 +31,6 @@ import org.elasticsearch.geometry.ShapeType; import org.elasticsearch.geometry.utils.StandardValidator; import org.elasticsearch.geometry.utils.WellKnownText; -import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper.AbstractGeometryFieldType; import java.io.IOException; import java.text.ParseException; From 28473c3e6aebd13bf99b7200c4094e81c8600105 Mon Sep 17 00:00:00 2001 From: Shubhangi Prasad Date: Mon, 15 Jun 2020 16:04:59 -0700 Subject: [PATCH 5/7] Update GeoBoundingBox.java --- .../main/java/org/elasticsearch/common/geo/GeoBoundingBox.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java index aee25500b0fb0..01315d3e9848c 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoBoundingBox.java @@ -54,7 +54,6 @@ public class GeoBoundingBox implements ToXContentObject, Writeable { public static final ParseField LON_FIELD = new ParseField("lon"); public static final ParseField TOP_LEFT_FIELD = new ParseField("top_left"); public static final ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right"); -// private AbstractGeometryFieldType abstractGeometryFieldType; private final GeoPoint topLeft; private final GeoPoint bottomRight; From 0c6d1b9ae6a5807aa4e09ead89cc1f8d2ff69273 Mon Sep 17 00:00:00 2001 From: Shubhangi Prasad Date: Mon, 15 Jun 2020 16:05:55 -0700 Subject: [PATCH 6/7] Update StreamInput.java --- .../java/org/elasticsearch/common/io/stream/StreamInput.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java index 9823a4ade9ac9..7de344d95dc46 100644 --- a/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java +++ b/server/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java @@ -41,7 +41,6 @@ import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; -import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper; import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.joda.time.DateTimeZone; @@ -832,9 +831,6 @@ public GeoPoint readGeoPoint() throws IOException { return new GeoPoint(readDouble(), readDouble()); } -// public AbstractGeometryFieldMapper.AbstractGeometryFieldType readAbstractGeomteryType() throws Exception{ -// return AbstractGeometryFieldMapper.AbstractGeometryFieldType(); -// } /** * Read a {@linkplain DateTimeZone}. */ From 5f978839ed67378e03d70093849bc164b9980196 Mon Sep 17 00:00:00 2001 From: shubhangi2901 Date: Thu, 18 Jun 2020 12:16:47 -0700 Subject: [PATCH 7/7] geodistance and geoboundingbox queries now inherit from AbstractGeometryQueryBuilder --- .../query/GeoBoundingBoxQueryBuilder.java | 306 +++++++++++---- .../index/query/GeoDistanceQueryBuilder.java | 359 ++++++++++++------ 2 files changed, 462 insertions(+), 203 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java index d30802db5dd58..4115cc4b7671c 100644 --- a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java @@ -21,6 +21,7 @@ import org.apache.lucene.document.LatLonDocValuesField; import org.apache.lucene.document.LatLonPoint; +import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; @@ -28,15 +29,17 @@ import org.elasticsearch.common.Numbers; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; -import org.elasticsearch.common.geo.GeoBoundingBox; -import org.elasticsearch.common.geo.GeoPoint; -import org.elasticsearch.common.geo.GeoUtils; +import org.elasticsearch.common.geo.*; +import org.elasticsearch.common.geo.builders.ShapeBuilder; +import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Rectangle; import org.elasticsearch.geometry.utils.Geohash; +import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType; import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.GeoPointFieldMapper.GeoPointFieldType; import org.elasticsearch.index.mapper.GeoShapeFieldMapper; @@ -47,6 +50,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; /** * Creates a Lucene query that will filter for all documents that lie within the specified @@ -55,7 +59,7 @@ * This query can only operate on fields of type geo_point that have latitude and longitude * enabled. * */ -public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder { +public class GeoBoundingBoxQueryBuilder extends AbstractGeometryQueryBuilder { public static final String NAME = "geo_bounding_box"; /** Default type for executing this query (memory as of this writing). */ @@ -72,7 +76,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder validContentTypes = Collections.unmodifiableList( Arrays.asList( @@ -95,23 +100,40 @@ protected List validContentTypes() { * Create new bounding box query. * @param fieldName name of index field containing geo coordinates to operate on. * */ - public GeoBoundingBoxQueryBuilder(String fieldName) { - if (fieldName == null) { - throw new IllegalArgumentException("Field name must not be empty."); - } - this.fieldName = fieldName; +// public GeoBoundingBoxQueryBuilder(String fieldName) { +// if (fieldName == null) { +// throw new IllegalArgumentException("Field name must not be empty."); +// } +// this.fieldName = fieldName; +// } + + public GeoBoundingBoxQueryBuilder(String fieldName, Geometry shape){ + super(fieldName, shape); } + + public GeoBoundingBoxQueryBuilder(String fieldName, Supplier shapeSupplier, String indexedShapeId) { + super(fieldName, shapeSupplier, indexedShapeId); + } /** * Read from a stream. */ public GeoBoundingBoxQueryBuilder(StreamInput in) throws IOException { super(in); - fieldName = in.readString(); +// fieldName = in.readString(); geoBoundingBox = new GeoBoundingBox(in); type = GeoExecType.readFromStream(in); validationMethod = GeoValidationMethod.readFromStream(in); ignoreUnmapped = in.readBoolean(); + strategy = in.readOptionalWriteable(SpatialStrategy::readFromStream); + } + @Deprecated + public GeoBoundingBoxQueryBuilder(String fieldName, ShapeBuilder shape) { + super(fieldName, shape); + } + + public GeoBoundingBoxQueryBuilder(String fieldName, String indexedShapeId){ + super(fieldName, indexedShapeId); } @Override @@ -317,20 +339,61 @@ QueryValidationException checkLatLon() { return validationException; } - @Override - public Query doToQuery(QueryShardContext context) { - MappedFieldType fieldType = context.fieldMapper(fieldName); - if (fieldType == null) { - if (ignoreUnmapped) { - return new MatchNoDocsQuery(); - } else { - throw new QueryShardException(context, "failed to find geo_point field [" + fieldName + "]"); - } - } -// if (!(fieldType instanceof GeoPointFieldType)) { -// throw new QueryShardException(context, "field [" + fieldName + "] is not a geo_point field"); +// @Override +// public Query doToQuery(QueryShardContext context) { +// MappedFieldType fieldType = context.fieldMapper(fieldName); +// if (fieldType == null) { +// if (ignoreUnmapped) { +// return new MatchNoDocsQuery(); +// } else { +// throw new QueryShardException(context, "failed to find geo_point field [" + fieldName + "]"); +// } +// } +//// if (!(fieldType instanceof GeoPointFieldType)) { +//// throw new QueryShardException(context, "field [" + fieldName + "] is not a geo_point field"); +//// } +// if (!validContentTypes().contains(fieldType.typeName())) { +// throw new QueryShardException(context, +// "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. [" +// + NAME + "] query supports the following types [" +// + String.join(",", validContentTypes()) + "]"); // } - if (!validContentTypes().contains(fieldType.typeName())) { +// QueryValidationException exception = checkLatLon(); +// if (exception != null) { +// throw new QueryShardException(context, "couldn't validate latitude/ longitude values", exception); +// } +// +// GeoPoint luceneTopLeft = new GeoPoint(geoBoundingBox.topLeft()); +// GeoPoint luceneBottomRight = new GeoPoint(geoBoundingBox.bottomRight()); +// 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 longitude range +// double right = luceneBottomRight.getLon(); +// double left = luceneTopLeft.getLon(); +// +// boolean completeLonRange = ((right - left) % 360 == 0 && right > left); +// GeoUtils.normalizePoint(luceneTopLeft, true, !completeLonRange); +// GeoUtils.normalizePoint(luceneBottomRight, true, !completeLonRange); +// if (completeLonRange) { +// luceneTopLeft.resetLon(-180); +// luceneBottomRight.resetLon(180); +// } +// } +// +// Query query = LatLonPoint.newBoxQuery(fieldType.name(), luceneBottomRight.getLat(), luceneTopLeft.getLat(), +// luceneTopLeft.getLon(), luceneBottomRight.getLon()); +// if (fieldType.hasDocValues()) { +// Query dvQuery = LatLonDocValuesField.newSlowBoxQuery(fieldType.name(), +// luceneBottomRight.getLat(), luceneTopLeft.getLat(), +// luceneTopLeft.getLon(), luceneBottomRight.getLon()); +// query = new IndexOrDocValuesQuery(query, dvQuery); +// } +// return query; +// } + + @Override + public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) { + if (!validContentTypes().contains(fieldType.typeName())) { throw new QueryShardException(context, "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. [" + NAME + "] query supports the following types [" @@ -357,16 +420,26 @@ public Query doToQuery(QueryShardContext context) { luceneBottomRight.resetLon(180); } } + final AbstractSearchableGeometryFieldType ft = + (AbstractSearchableGeometryFieldType) fieldType; + return new ConstantScoreQuery(ft.geometryQueryBuilder().process(shape, fieldName, strategy, relation, context)); + } - Query query = LatLonPoint.newBoxQuery(fieldType.name(), luceneBottomRight.getLat(), luceneTopLeft.getLat(), - luceneTopLeft.getLon(), luceneBottomRight.getLon()); - if (fieldType.hasDocValues()) { - Query dvQuery = LatLonDocValuesField.newSlowBoxQuery(fieldType.name(), - luceneBottomRight.getLat(), luceneTopLeft.getLat(), - luceneTopLeft.getLon(), luceneBottomRight.getLon()); - query = new IndexOrDocValuesQuery(query, dvQuery); + @Override + protected void doShapeQueryXContent(XContentBuilder builder, Params params) throws IOException { + if (strategy != null) { + builder.field(STRATEGY_FIELD.getPreferredName(), strategy.getStrategyName()); } - return query; + } + + @Override + protected GeoBoundingBoxQueryBuilder newShapeQueryBuilder(String fieldName, Geometry shape) { + return new GeoBoundingBoxQueryBuilder(fieldName, shape); + } + + @Override + protected GeoBoundingBoxQueryBuilder newShapeQueryBuilder(String fieldName, Supplier shapeSupplier, String indexedShapeId) { + return new GeoBoundingBoxQueryBuilder(fieldName, shapeSupplier, indexedShapeId); } @Override @@ -385,62 +458,133 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep builder.endObject(); } - public static GeoBoundingBoxQueryBuilder fromXContent(XContentParser parser) throws IOException { - String fieldName = null; - - float boost = AbstractQueryBuilder.DEFAULT_BOOST; - String queryName = null; - String currentFieldName = null; - XContentParser.Token token; - GeoValidationMethod validationMethod = null; - boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED; - - // bottom (minLat), top (maxLat), left (minLon), right (maxLon) - GeoBoundingBox bbox = null; - String type = "memory"; - - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.START_OBJECT) { - try { - bbox = GeoBoundingBox.parseBoundingBox(parser); - fieldName = currentFieldName; - } catch (Exception e) { - throw new ElasticsearchParseException("failed to parse [{}] query. [{}]", NAME, e.getMessage()); - } - } else if (token.isValue()) { - if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - queryName = parser.text(); - } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - boost = parser.floatValue(); - } else if (VALIDATION_METHOD_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - validationMethod = GeoValidationMethod.fromString(parser.text()); - } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - ignoreUnmapped = parser.booleanValue(); - } else if (TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - type = parser.text(); +// public static GeoBoundingBoxQueryBuilder fromXContent(XContentParser parser) throws IOException { +// String fieldName = null; +// +// float boost = AbstractQueryBuilder.DEFAULT_BOOST; +// String queryName = null; +// String currentFieldName = null; +// XContentParser.Token token; +// GeoValidationMethod validationMethod = null; +// boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED; +// +// // bottom (minLat), top (maxLat), left (minLon), right (maxLon) +// GeoBoundingBox bbox = null; +// String type = "memory"; +// +// while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { +// if (token == XContentParser.Token.FIELD_NAME) { +// currentFieldName = parser.currentName(); +// } else if (token == XContentParser.Token.START_OBJECT) { +// try { +// bbox = GeoBoundingBox.parseBoundingBox(parser); +// fieldName = currentFieldName; +// } catch (Exception e) { +// throw new ElasticsearchParseException("failed to parse [{}] query. [{}]", NAME, e.getMessage()); +// } +// } else if (token.isValue()) { +// if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// queryName = parser.text(); +// } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// boost = parser.floatValue(); +// } else if (VALIDATION_METHOD_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// validationMethod = GeoValidationMethod.fromString(parser.text()); +// } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// ignoreUnmapped = parser.booleanValue(); +// } else if (TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// type = parser.text(); +// } else { +// throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. unexpected field [{}]", +// NAME, currentFieldName); +// } +// } +// } +// +// if (bbox == null) { +// throw new ElasticsearchParseException("failed to parse [{}] query. bounding box not provided", NAME); +// } +// +// GeoBoundingBoxQueryBuilder builder = new GeoBoundingBoxQueryBuilder(fieldName, ); +// builder.setCorners(bbox.topLeft(), bbox.bottomRight()); +// builder.queryName(queryName); +// builder.boost(boost); +// builder.type(GeoExecType.fromString(type)); +// builder.ignoreUnmapped(ignoreUnmapped); +// if (validationMethod != null) { +// // ignore deprecated coerce/ignoreMalformed settings if validationMethod is set +// builder.setValidationMethod(validationMethod); +// } +// return builder; +// } + public GeoBoundingBoxQueryBuilder strategy(SpatialStrategy strategy) { + if (strategy != null && strategy == SpatialStrategy.TERM && relation != ShapeRelation.INTERSECTS) { + throw new IllegalArgumentException("strategy [" + strategy.getStrategyName() + "] only supports relation [" + + ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]"); + } + this.strategy = strategy; + return this; + } + + private static class ParsedGeoShapeQueryParams extends ParsedGeometryQueryParams { + SpatialStrategy strategy; + + @Override + protected boolean parseXContentField(XContentParser parser) throws IOException { + SpatialStrategy strategy; + if (SHAPE_FIELD.match(parser.currentName(), parser.getDeprecationHandler())) { + this.shape = ShapeParser.parse(parser); + return true; + } else if (STRATEGY_FIELD.match(parser.currentName(), parser.getDeprecationHandler())) { + String strategyName = parser.text(); + strategy = SpatialStrategy.fromString(strategyName); + if (strategy == null) { + throw new ParsingException(parser.getTokenLocation(), "Unknown strategy [" + strategyName + " ]"); } else { - throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. unexpected field [{}]", - NAME, currentFieldName); + this.strategy = strategy; } + return true; } + return false; + } + } + public static GeoBoundingBoxQueryBuilder fromXContent(XContentParser parser) throws IOException { + ParsedGeoShapeQueryParams pgsqp = + (ParsedGeoShapeQueryParams) AbstractGeometryQueryBuilder.parsedParamsFromXContent(parser, new ParsedGeoShapeQueryParams()); + + GeoBoundingBoxQueryBuilder builder; + + if (pgsqp.shape != null) { + builder = new GeoBoundingBoxQueryBuilder(pgsqp.fieldName, pgsqp.shape); + } else { + builder = new GeoBoundingBoxQueryBuilder(pgsqp.fieldName, pgsqp.id); + } + + if (pgsqp.index != null) { + builder.indexedShapeIndex(pgsqp.index); + } + + if (pgsqp.shapePath != null) { + builder.indexedShapePath(pgsqp.shapePath); + } + + if (pgsqp.shapeRouting != null) { + builder.indexedShapeRouting(pgsqp.shapeRouting); } - if (bbox == null) { - throw new ElasticsearchParseException("failed to parse [{}] query. bounding box not provided", NAME); + if (pgsqp.relation != null) { + builder.relation(pgsqp.relation); } - GeoBoundingBoxQueryBuilder builder = new GeoBoundingBoxQueryBuilder(fieldName); - builder.setCorners(bbox.topLeft(), bbox.bottomRight()); - builder.queryName(queryName); - builder.boost(boost); - builder.type(GeoExecType.fromString(type)); - builder.ignoreUnmapped(ignoreUnmapped); - if (validationMethod != null) { - // ignore deprecated coerce/ignoreMalformed settings if validationMethod is set - builder.setValidationMethod(validationMethod); + if (pgsqp.strategy != null) { + builder.strategy(pgsqp.strategy); } + + if (pgsqp.queryName != null) { + builder.queryName(pgsqp.queryName); + } + + builder.boost(pgsqp.boost); + builder.ignoreUnmapped(pgsqp.ignoreUnmapped); return builder; } diff --git a/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java index 0e69731091f4e..2fcf4f5bb2718 100644 --- a/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java @@ -21,20 +21,23 @@ import org.apache.lucene.document.LatLonDocValuesField; import org.apache.lucene.document.LatLonPoint; +import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.geo.GeoDistance; -import org.elasticsearch.common.geo.GeoPoint; -import org.elasticsearch.common.geo.GeoUtils; +import org.elasticsearch.common.geo.*; +import org.elasticsearch.common.geo.builders.ShapeBuilder; +import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.geometry.Geometry; +import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType; import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.GeoPointFieldMapper.GeoPointFieldType; import org.elasticsearch.index.mapper.GeoShapeFieldMapper; @@ -42,12 +45,13 @@ import java.io.IOException; import java.util.*; +import java.util.function.Supplier; /** * Filter results of a query to include only those within a specific distance to some * geo point. */ -public class GeoDistanceQueryBuilder extends AbstractQueryBuilder { +public class GeoDistanceQueryBuilder extends AbstractGeometryQueryBuilder { public static final String NAME = "geo_distance"; /** Default for distance unit computation. */ @@ -66,7 +70,7 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder validContentTypes = Collections.unmodifiableList( Arrays.asList( @@ -91,24 +97,31 @@ protected List validContentTypes() { * Construct new GeoDistanceQueryBuilder. * @param fieldName name of indexed geo field to operate distance computation on. * */ - public GeoDistanceQueryBuilder(String fieldName) { - if (Strings.isEmpty(fieldName)) { - throw new IllegalArgumentException("fieldName must not be null or empty"); - } - this.fieldName = fieldName; + public GeoDistanceQueryBuilder(String fieldName, String indexedShapeId) { + super(fieldName, indexedShapeId); + } + public GeoDistanceQueryBuilder(String fieldName, Geometry geometry){ + super(fieldName, geometry); } + public GeoDistanceQueryBuilder(String fieldName, Supplier geometrySupplier ,String indexedShapeId){ + super(fieldName, geometrySupplier, indexedShapeId); + } + public GeoDistanceQueryBuilder(String fieldName, ShapeBuilder shapeBuilder) { + super(fieldName, shapeBuilder); + } /** * Read from a stream. */ public GeoDistanceQueryBuilder(StreamInput in) throws IOException { super(in); - fieldName = in.readString(); +// fieldName = in.readString(); distance = in.readDouble(); validationMethod = GeoValidationMethod.readFromStream(in); center = in.readGeoPoint(); geoDistance = GeoDistance.readFromStream(in); ignoreUnmapped = in.readBoolean(); + strategy = in.readOptionalWriteable(SpatialStrategy::readFromStream); } @Override @@ -121,6 +134,15 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeBoolean(ignoreUnmapped); } + public GeoDistanceQueryBuilder strategy(SpatialStrategy strategy) { + if (strategy != null && strategy == SpatialStrategy.TERM && relation != ShapeRelation.INTERSECTS) { + throw new IllegalArgumentException("strategy [" + strategy.getStrategyName() + "] only supports relation [" + + ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]"); + } + this.strategy = strategy; + return this; + } + /** Name of the field this query is operating on. */ public String fieldName() { return this.fieldName; @@ -235,43 +257,73 @@ public boolean ignoreUnmapped() { return ignoreUnmapped; } - @Override - protected Query doToQuery(QueryShardContext shardContext) throws IOException { - MappedFieldType fieldType = shardContext.fieldMapper(fieldName); - if (fieldType == null) { - if (ignoreUnmapped) { - return new MatchNoDocsQuery(); - } else { - throw new QueryShardException(shardContext, "failed to find geo_point field [" + fieldName + "]"); - } - } +// @Override +// protected Query doToQuery(QueryShardContext shardContext) throws IOException { +// MappedFieldType fieldType = shardContext.fieldMapper(fieldName); +// if (fieldType == null) { +// if (ignoreUnmapped) { +// return new MatchNoDocsQuery(); +// } else { +// throw new QueryShardException(shardContext, "failed to find geo_point field [" + fieldName + "]"); +// } +// } // if (!(fieldType instanceof GeoPointFieldType)) { // throw new QueryShardException(shardContext, "field [" + fieldName + "] is not a geo_point field"); // } +// if (!validContentTypes().contains(fieldType.typeName())) { +// throw new QueryShardException(shardContext, +// "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. [" +// + NAME + "] query supports the following types [" +// + String.join(",", validContentTypes()) + "]"); +// } +// +// QueryValidationException exception = checkLatLon(); +// if (exception != null) { +// throw new QueryShardException(shardContext, "couldn't validate latitude/ longitude values", exception); +// } +// +// if (GeoValidationMethod.isCoerce(validationMethod)) { +// GeoUtils.normalizePoint(center, true, true); +// } + +// Query query = LatLonPoint.newDistanceQuery(fieldType.name(), center.lat(), center.lon(), this.distance); +// if (fieldType.hasDocValues()) { +// Query dvQuery = LatLonDocValuesField.newSlowDistanceQuery(fieldType.name(), center.lat(), center.lon(), this.distance); +// query = new IndexOrDocValuesQuery(query, dvQuery); +// } +// return query; +// } + @Override + public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) { if (!validContentTypes().contains(fieldType.typeName())) { - throw new QueryShardException(shardContext, + throw new QueryShardException(context, "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. [" + NAME + "] query supports the following types [" + String.join(",", validContentTypes()) + "]"); } - QueryValidationException exception = checkLatLon(); - if (exception != null) { - throw new QueryShardException(shardContext, "couldn't validate latitude/ longitude values", exception); - } + final AbstractSearchableGeometryFieldType ft = + (AbstractSearchableGeometryFieldType) fieldType; + return new ConstantScoreQuery(ft.geometryQueryBuilder().process(shape, fieldName, strategy, relation, context)); + } - if (GeoValidationMethod.isCoerce(validationMethod)) { - GeoUtils.normalizePoint(center, true, true); + @Override + protected void doShapeQueryXContent(XContentBuilder builder, Params params) throws IOException { + if (strategy != null) { + builder.field(STRATEGY_FIELD.getPreferredName(), strategy.getStrategyName()); } + } - Query query = LatLonPoint.newDistanceQuery(fieldType.name(), center.lat(), center.lon(), this.distance); - if (fieldType.hasDocValues()) { - Query dvQuery = LatLonDocValuesField.newSlowDistanceQuery(fieldType.name(), center.lat(), center.lon(), this.distance); - query = new IndexOrDocValuesQuery(query, dvQuery); - } - return query; + @Override + protected GeoDistanceQueryBuilder newShapeQueryBuilder(String fieldName, Geometry shape) { + return new GeoDistanceQueryBuilder(fieldName, shape); + } + + @Override + protected GeoDistanceQueryBuilder newShapeQueryBuilder(String fieldName, Supplier shapeSupplier, String indexedShapeId) { + return new GeoDistanceQueryBuilder(fieldName, shapeSupplier, indexedShapeId); } @Override @@ -286,105 +338,168 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep builder.endObject(); } - public static GeoDistanceQueryBuilder fromXContent(XContentParser parser) throws IOException { - XContentParser.Token token; - - float boost = AbstractQueryBuilder.DEFAULT_BOOST; - String queryName = null; - String currentFieldName = null; - GeoPoint point = new GeoPoint(Double.NaN, Double.NaN); - String fieldName = null; - Object vDistance = null; - DistanceUnit unit = GeoDistanceQueryBuilder.DEFAULT_DISTANCE_UNIT; - GeoDistance geoDistance = GeoDistanceQueryBuilder.DEFAULT_GEO_DISTANCE; - GeoValidationMethod validationMethod = null; - boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED; - - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.START_ARRAY) { - fieldName = currentFieldName; - GeoUtils.parseGeoPoint(parser, point); - } else if (token == XContentParser.Token.START_OBJECT) { - throwParsingExceptionOnMultipleFields(NAME, parser.getTokenLocation(), fieldName, currentFieldName); - // the json in the format of -> field : { lat : 30, lon : 12 } - String currentName = parser.currentName(); - fieldName = currentFieldName; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentName = parser.currentName(); - } else if (token.isValue()) { - if (currentName.equals("lat")) { - point.resetLat(parser.doubleValue()); - } else if (currentName.equals("lon")) { - point.resetLon(parser.doubleValue()); - } else if (currentName.equals("geohash")) { - point.resetFromGeoHash(parser.text()); - } else { - throw new ParsingException(parser.getTokenLocation(), - "[geo_distance] query does not support [" + currentFieldName + "]"); - } - } - } - } else if (token.isValue()) { - if (DISTANCE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - if (token == XContentParser.Token.VALUE_STRING) { - vDistance = parser.text(); // a String - } else { - vDistance = parser.numberValue(); // a Number - } - } else if (UNIT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - unit = DistanceUnit.fromString(parser.text()); - } else if (DISTANCE_TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - geoDistance = GeoDistance.fromString(parser.text()); - } else if (currentFieldName.endsWith(".lat")) { - point.resetLat(parser.doubleValue()); - fieldName = currentFieldName.substring(0, currentFieldName.length() - ".lat".length()); - } else if (currentFieldName.endsWith(".lon")) { - point.resetLon(parser.doubleValue()); - fieldName = currentFieldName.substring(0, currentFieldName.length() - ".lon".length()); - } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - queryName = parser.text(); - } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - boost = parser.floatValue(); - } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - ignoreUnmapped = parser.booleanValue(); - } else if (VALIDATION_METHOD_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - validationMethod = GeoValidationMethod.fromString(parser.text()); +// public static GeoDistanceQueryBuilder fromXContent(XContentParser parser) throws IOException { +// XContentParser.Token token; +// +// float boost = AbstractQueryBuilder.DEFAULT_BOOST; +// String queryName = null; +// String currentFieldName = null; +// GeoPoint point = new GeoPoint(Double.NaN, Double.NaN); +// String fieldName = null; +// Object vDistance = null; +// DistanceUnit unit = GeoDistanceQueryBuilder.DEFAULT_DISTANCE_UNIT; +// GeoDistance geoDistance = GeoDistanceQueryBuilder.DEFAULT_GEO_DISTANCE; +// GeoValidationMethod validationMethod = null; +// boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED; +// +// while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { +// if (token == XContentParser.Token.FIELD_NAME) { +// currentFieldName = parser.currentName(); +// } else if (token == XContentParser.Token.START_ARRAY) { +// fieldName = currentFieldName; +// GeoUtils.parseGeoPoint(parser, point); +// } else if (token == XContentParser.Token.START_OBJECT) { +// throwParsingExceptionOnMultipleFields(NAME, parser.getTokenLocation(), fieldName, currentFieldName); +// // the json in the format of -> field : { lat : 30, lon : 12 } +// String currentName = parser.currentName(); +// fieldName = currentFieldName; +// while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { +// if (token == XContentParser.Token.FIELD_NAME) { +// currentName = parser.currentName(); +// } else if (token.isValue()) { +// if (currentName.equals("lat")) { +// point.resetLat(parser.doubleValue()); +// } else if (currentName.equals("lon")) { +// point.resetLon(parser.doubleValue()); +// } else if (currentName.equals("geohash")) { +// point.resetFromGeoHash(parser.text()); +// } else { +// throw new ParsingException(parser.getTokenLocation(), +// "[geo_distance] query does not support [" + currentFieldName + "]"); +// } +// } +// } +// } else if (token.isValue()) { +// if (DISTANCE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// if (token == XContentParser.Token.VALUE_STRING) { +// vDistance = parser.text(); // a String +// } else { +// vDistance = parser.numberValue(); // a Number +// } +// } else if (UNIT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// unit = DistanceUnit.fromString(parser.text()); +// } else if (DISTANCE_TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// geoDistance = GeoDistance.fromString(parser.text()); +// } else if (currentFieldName.endsWith(".lat")) { +// point.resetLat(parser.doubleValue()); +// fieldName = currentFieldName.substring(0, currentFieldName.length() - ".lat".length()); +// } else if (currentFieldName.endsWith(".lon")) { +// point.resetLon(parser.doubleValue()); +// fieldName = currentFieldName.substring(0, currentFieldName.length() - ".lon".length()); +// } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// queryName = parser.text(); +// } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// boost = parser.floatValue(); +// } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// ignoreUnmapped = parser.booleanValue(); +// } else if (VALIDATION_METHOD_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { +// validationMethod = GeoValidationMethod.fromString(parser.text()); +// } else { +// if (fieldName == null) { +// point.resetFromString(parser.text()); +// fieldName = currentFieldName; +// } else { +// throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. unexpected field [{}]", +// NAME, currentFieldName); +// } +// } +// } +// } +// +// if (vDistance == null) { +// throw new ParsingException(parser.getTokenLocation(), "geo_distance requires 'distance' to be specified"); +// } +// +// GeoDistanceQueryBuilder qb = new GeoDistanceQueryBuilder(fieldName); +// if (vDistance instanceof Number) { +// qb.distance(((Number) vDistance).doubleValue(), unit); +// } else { +// qb.distance((String) vDistance, unit); +// } +// qb.point(point); +// if (validationMethod != null) { +// qb.setValidationMethod(validationMethod); +// } +// qb.geoDistance(geoDistance); +// qb.boost(boost); +// qb.queryName(queryName); +// qb.ignoreUnmapped(ignoreUnmapped); +// return qb; +// } + private static class ParsedGeoShapeQueryParams extends ParsedGeometryQueryParams { + SpatialStrategy strategy; + + @Override + protected boolean parseXContentField(XContentParser parser) throws IOException { + SpatialStrategy strategy; + if (SHAPE_FIELD.match(parser.currentName(), parser.getDeprecationHandler())) { + this.shape = ShapeParser.parse(parser); + return true; + } else if (STRATEGY_FIELD.match(parser.currentName(), parser.getDeprecationHandler())) { + String strategyName = parser.text(); + strategy = SpatialStrategy.fromString(strategyName); + if (strategy == null) { + throw new ParsingException(parser.getTokenLocation(), "Unknown strategy [" + strategyName + " ]"); } else { - if (fieldName == null) { - point.resetFromString(parser.text()); - fieldName = currentFieldName; - } else { - throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. unexpected field [{}]", - NAME, currentFieldName); - } + this.strategy = strategy; } + return true; } + return false; } + } + public static GeoDistanceQueryBuilder fromXContent(XContentParser parser) throws IOException { + ParsedGeoShapeQueryParams pgsqp = + (ParsedGeoShapeQueryParams) AbstractGeometryQueryBuilder.parsedParamsFromXContent(parser, new ParsedGeoShapeQueryParams()); - if (vDistance == null) { - throw new ParsingException(parser.getTokenLocation(), "geo_distance requires 'distance' to be specified"); - } + GeoDistanceQueryBuilder builder; - GeoDistanceQueryBuilder qb = new GeoDistanceQueryBuilder(fieldName); - if (vDistance instanceof Number) { - qb.distance(((Number) vDistance).doubleValue(), unit); + if (pgsqp.shape != null) { + builder = new GeoDistanceQueryBuilder(pgsqp.fieldName, pgsqp.shape); } else { - qb.distance((String) vDistance, unit); + builder = new GeoDistanceQueryBuilder(pgsqp.fieldName, pgsqp.id); + } + + if (pgsqp.index != null) { + builder.indexedShapeIndex(pgsqp.index); + } + + if (pgsqp.shapePath != null) { + builder.indexedShapePath(pgsqp.shapePath); + } + + if (pgsqp.shapeRouting != null) { + builder.indexedShapeRouting(pgsqp.shapeRouting); + } + + if (pgsqp.relation != null) { + builder.relation(pgsqp.relation); + } + + if (pgsqp.strategy != null) { + builder.strategy(pgsqp.strategy); } - qb.point(point); - if (validationMethod != null) { - qb.setValidationMethod(validationMethod); + + if (pgsqp.queryName != null) { + builder.queryName(pgsqp.queryName); } - qb.geoDistance(geoDistance); - qb.boost(boost); - qb.queryName(queryName); - qb.ignoreUnmapped(ignoreUnmapped); - return qb; + + builder.boost(pgsqp.boost); + builder.ignoreUnmapped(pgsqp.ignoreUnmapped); + return builder; } + @Override protected int doHashCode() { return Objects.hash(center, geoDistance, distance, validationMethod, ignoreUnmapped);