diff --git a/docs/reference/query-dsl/geo-bounding-box-query.asciidoc b/docs/reference/query-dsl/geo-bounding-box-query.asciidoc
index ca355413b2e5c..afa1175055719 100644
--- a/docs/reference/query-dsl/geo-bounding-box-query.asciidoc
+++ b/docs/reference/query-dsl/geo-bounding-box-query.asciidoc
@@ -4,8 +4,13 @@
Geo-bounding box
++++
-A query allowing to filter hits based on a point location using a
-bounding box. Assuming the following indexed document:
+Matches <> and <> values that
+intersect a bounding box.
+
+[discrete]
+[[geo-bounding-box-query-ex]]
+==== Example
+Assume the following the following documents are indexed:
[source,console]
--------------------------------------------------
@@ -33,11 +38,36 @@ PUT /my_locations/_doc/1
}
}
}
+
+PUT /my_geoshapes
+{
+ "mappings": {
+ "properties": {
+ "pin": {
+ "properties": {
+ "location": {
+ "type": "geo_shape"
+ }
+ }
+ }
+ }
+ }
+}
+
+PUT /my_geoshapes/_doc/1
+{
+ "pin": {
+ "location": {
+ "type" : "polygon",
+ "coordinates" : [[[13.0 ,51.5], [15.0, 51.5], [15.0, 54.0], [13.0, 54.0], [13.0 ,51.5]]]
+ }
+ }
+}
--------------------------------------------------
// TESTSETUP
-Then the following simple query can be executed with a
-`geo_bounding_box` filter:
+Use a `geo_bounding_box` filter to match `geo_point` values that intersect a bounding
+box. To define the box, provide geopoint values for two opposite corners.
[source,console]
--------------------------------------------------
@@ -67,6 +97,66 @@ GET my_locations/_search
}
--------------------------------------------------
+Use the same filter to match `geo_shape` values that intersect the bounding box:
+
+[source,console]
+--------------------------------------------------
+GET my_geoshapes/_search
+{
+ "query": {
+ "bool": {
+ "must": {
+ "match_all": {}
+ },
+ "filter": {
+ "geo_bounding_box": {
+ "pin.location": {
+ "top_left": {
+ "lat": 40.73,
+ "lon": -74.1
+ },
+ "bottom_right": {
+ "lat": 40.01,
+ "lon": -71.12
+ }
+ }
+ }
+ }
+ }
+ }
+}
+--------------------------------------------------
+
+To match both `geo_point` and `geo_shape` values, search both indices:
+
+[source,console]
+--------------------------------------------------
+GET my_locations,my_geoshapes/_search
+{
+ "query": {
+ "bool": {
+ "must": {
+ "match_all": {}
+ },
+ "filter": {
+ "geo_bounding_box": {
+ "pin.location": {
+ "top_left": {
+ "lat": 40.73,
+ "lon": -74.1
+ },
+ "bottom_right": {
+ "lat": 40.01,
+ "lon": -71.12
+ }
+ }
+ }
+ }
+ }
+ }
+}
+--------------------------------------------------
+
[discrete]
==== Query Options
@@ -291,13 +381,6 @@ GET my_locations/_search
}
--------------------------------------------------
-
-[discrete]
-==== geo_point Type
-
-The filter *requires* the `geo_point` type to be set on the relevant
-field.
-
[discrete]
==== Multi Location Per Document
@@ -366,3 +449,8 @@ the upper bounds (top and right edges) might be selected by the query even if
they are located slightly outside the edge. The rounding error should be less
than 4.20e-8 degrees on the latitude and less than 8.39e-8 degrees on the
longitude, which translates to less than 1cm error even at the equator.
+
+Geoshapes also have limited precision due to rounding. Geoshape edges along the
+bounding box's bottom and left edges may not match a `geo_bounding_box` query.
+Geoshape edges slightly outside the box's top and right edges may still match
+the query.
diff --git a/docs/reference/query-dsl/geo-distance-query.asciidoc b/docs/reference/query-dsl/geo-distance-query.asciidoc
index cfb2779659e2b..be76c24402c7a 100644
--- a/docs/reference/query-dsl/geo-distance-query.asciidoc
+++ b/docs/reference/query-dsl/geo-distance-query.asciidoc
@@ -4,9 +4,14 @@
Geo-distance
++++
-Filters documents that include only hits that exists within a specific
-distance from a geo point. Assuming the following mapping and indexed
-document:
+Matches <> and <> values within
+a given distance of a geopoint.
+
+[discrete]
+[[geo-distance-query-ex]]
+==== Example
+
+Assume the following the following documents are indexed:
[source,console]
--------------------------------------------------
@@ -34,12 +39,37 @@ PUT /my_locations/_doc/1
}
}
}
+
+PUT /my_geoshapes
+{
+ "mappings": {
+ "properties": {
+ "pin": {
+ "properties": {
+ "location": {
+ "type": "geo_shape"
+ }
+ }
+ }
+ }
+ }
+}
+
+PUT /my_geoshapes/_doc/1
+{
+ "pin": {
+ "location": {
+ "type" : "polygon",
+ "coordinates" : [[[13.0 ,51.5], [15.0, 51.5], [15.0, 54.0], [13.0, 54.0], [13.0 ,51.5]]]
+ }
+ }
+}
--------------------------------------------------
// TESTSETUP
-Then the following simple query can be executed with a `geo_distance`
-filter:
+Use a `geo_distance` filter to match `geo_point` values within a specified
+distance of another geopoint:
[source,console]
--------------------------------------------------
@@ -64,6 +94,57 @@ GET /my_locations/_search
}
--------------------------------------------------
+Use the same filter to match `geo_shape` values within the given distance:
+
+[source,console]
+--------------------------------------------------
+GET my_geoshapes/_search
+{
+ "query": {
+ "bool": {
+ "must": {
+ "match_all": {}
+ },
+ "filter": {
+ "geo_distance": {
+ "distance": "200km",
+ "pin.location": {
+ "lat": 40,
+ "lon": -70
+ }
+ }
+ }
+ }
+ }
+}
+--------------------------------------------------
+
+To match both `geo_point` and `geo_shape` values, search both indices:
+
+[source,console]
+--------------------------------------------------
+GET my_locations,my_geoshapes/_search
+{
+ "query": {
+ "bool": {
+ "must": {
+ "match_all": {}
+ },
+ "filter": {
+ "geo_distance": {
+ "distance": "200km",
+ "pin.location": {
+ "lat": 40,
+ "lon": -70
+ }
+ }
+ }
+ }
+ }
+}
+--------------------------------------------------
+
+
[discrete]
==== Accepted Formats
@@ -198,12 +279,6 @@ The following are options allowed on the filter:
longitude, set to `COERCE` to additionally try and infer correct
coordinates (default is `STRICT`).
-[discrete]
-==== geo_point Type
-
-The filter *requires* the `geo_point` type to be set on the relevant
-field.
-
[discrete]
==== Multi Location Per Document
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/AbstractGeoBoundingBoxQueryIT.java
similarity index 79%
rename from server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIT.java
rename to server/src/internalClusterTest/java/org/elasticsearch/search/geo/AbstractGeoBoundingBoxQueryIT.java
index 94b40a5fd7268..cebc61a28d908 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/AbstractGeoBoundingBoxQueryIT.java
@@ -7,7 +7,7 @@
* 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
+ * 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
@@ -23,77 +23,80 @@
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.unit.DistanceUnit;
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.elasticsearch.test.VersionUtils;
+import java.io.IOException;
+
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.geoBoundingBoxQuery;
+import static org.elasticsearch.index.query.QueryBuilders.geoDistanceQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
-public class GeoBoundingBoxQueryIT extends ESIntegTestCase {
+abstract class AbstractGeoBoundingBoxQueryIT extends ESIntegTestCase {
@Override
protected boolean forbidPrivateIndexSettings() {
return false;
}
+ public abstract XContentBuilder getMapping() throws IOException;
+
public void testSimpleBoundingBoxTest() throws Exception {
Version version = VersionUtils.randomIndexCompatibleVersion(random());
Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build();
- XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("_doc")
- .startObject("properties").startObject("location").field("type", "geo_point");
- xContentBuilder.endObject().endObject().endObject().endObject();
+ XContentBuilder xContentBuilder = getMapping();
assertAcked(prepareCreate("test").setSettings(settings).setMapping(xContentBuilder));
ensureGreen();
client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject()
.field("name", "New York")
- .startObject("location").field("lat", 40.7143528).field("lon", -74.0059731).endObject()
+ .field("location", "POINT(-74.0059731 40.7143528)")
.endObject()).get();
// to NY: 5.286 km
client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject()
.field("name", "Times Square")
- .startObject("location").field("lat", 40.759011).field("lon", -73.9844722).endObject()
+ .field("location", "POINT(-73.9844722 40.759011)")
.endObject()).get();
// to NY: 0.4621 km
client().prepareIndex("test").setId("3").setSource(jsonBuilder().startObject()
.field("name", "Tribeca")
- .startObject("location").field("lat", 40.718266).field("lon", -74.007819).endObject()
+ .field("location", "POINT(-74.007819 40.718266)")
.endObject()).get();
// to NY: 1.055 km
client().prepareIndex("test").setId("4").setSource(jsonBuilder().startObject()
.field("name", "Wall Street")
- .startObject("location").field("lat", 40.7051157).field("lon", -74.0088305).endObject()
+ .field("location", "POINT(-74.0088305 40.7051157)")
.endObject()).get();
// to NY: 1.258 km
client().prepareIndex("test").setId("5").setSource(jsonBuilder().startObject()
.field("name", "Soho")
- .startObject("location").field("lat", 40.7247222).field("lon", -74).endObject()
+ .field("location", "POINT(-74 40.7247222)")
.endObject()).get();
// to NY: 2.029 km
client().prepareIndex("test").setId("6").setSource(jsonBuilder().startObject()
.field("name", "Greenwich Village")
- .startObject("location").field("lat", 40.731033).field("lon", -73.9962255).endObject()
+ .field("location", "POINT(-73.9962255 40.731033)")
.endObject()).get();
// to NY: 8.572 km
client().prepareIndex("test").setId("7").setSource(jsonBuilder().startObject()
.field("name", "Brooklyn")
- .startObject("location").field("lat", 40.65).field("lon", -73.95).endObject()
+ .field("location", "POINT(-73.95 40.65)")
.endObject()).get();
client().admin().indices().prepareRefresh().get();
@@ -115,21 +118,28 @@ public void testSimpleBoundingBoxTest() throws Exception {
for (SearchHit hit : searchResponse.getHits()) {
assertThat(hit.getId(), anyOf(equalTo("1"), equalTo("3"), equalTo("5")));
}
+ // Distance query
+ searchResponse = client().prepareSearch() // from NY
+ .setQuery(geoDistanceQuery("location").point(40.5, -73.9).distance(25, DistanceUnit.KILOMETERS))
+ .get();
+ assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
+ assertThat(searchResponse.getHits().getHits().length, equalTo(2));
+ for (SearchHit hit : searchResponse.getHits()) {
+ assertThat(hit.getId(), anyOf(equalTo("7"), equalTo("4")));
+ }
}
public void testLimit2BoundingBox() throws Exception {
Version version = VersionUtils.randomIndexCompatibleVersion(random());
Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build();
- XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("_doc")
- .startObject("properties").startObject("location").field("type", "geo_point");
- xContentBuilder.endObject().endObject().endObject().endObject();
+ XContentBuilder xContentBuilder = getMapping();
assertAcked(prepareCreate("test").setSettings(settings).setMapping(xContentBuilder));
ensureGreen();
client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject()
.field("userid", 880)
.field("title", "Place in Stockholm")
- .startObject("location").field("lat", 59.328355000000002).field("lon", 18.036842).endObject()
+ .field("location", "POINT(59.328355000000002 18.036842)")
.endObject())
.setRefreshPolicy(IMMEDIATE)
.get();
@@ -137,7 +147,7 @@ public void testLimit2BoundingBox() throws Exception {
client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject()
.field("userid", 534)
.field("title", "Place in Montreal")
- .startObject("location").field("lat", 45.509526999999999).field("lon", -73.570986000000005).endObject()
+ .field("location", "POINT(-73.570986000000005 45.509526999999999)")
.endObject())
.setRefreshPolicy(IMMEDIATE)
.get();
@@ -169,21 +179,34 @@ public void testLimit2BoundingBox() throws Exception {
.type("indexed"))
).get();
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+
+ // Distance query
+ searchResponse = client().prepareSearch()
+ .setQuery(
+ boolQuery().must(termQuery("userid", 880)).filter(
+ geoDistanceQuery("location").point(20, 60.0).distance(500, DistanceUnit.MILES))
+ ).get();
+ assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+
+ searchResponse = client().prepareSearch()
+ .setQuery(
+ boolQuery().must(termQuery("userid", 534)).filter(
+ geoDistanceQuery("location").point(45.0, -73.0).distance(500, DistanceUnit.MILES))
+ ).get();
+ assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
}
public void testCompleteLonRange() throws Exception {
Version version = VersionUtils.randomIndexCompatibleVersion(random());
Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build();
- XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("_doc")
- .startObject("properties").startObject("location").field("type", "geo_point");
- xContentBuilder.endObject().endObject().endObject().endObject();
+ XContentBuilder xContentBuilder = getMapping();
assertAcked(prepareCreate("test").setSettings(settings).setMapping(xContentBuilder));
ensureGreen();
client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject()
.field("userid", 880)
.field("title", "Place in Stockholm")
- .startObject("location").field("lat", 59.328355000000002).field("lon", 18.036842).endObject()
+ .field("location", "POINT(18.036842 59.328355000000002)")
.endObject())
.setRefreshPolicy(IMMEDIATE)
.get();
@@ -191,7 +214,7 @@ public void testCompleteLonRange() throws Exception {
client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject()
.field("userid", 534)
.field("title", "Place in Montreal")
- .startObject("location").field("lat", 45.509526999999999).field("lon", -73.570986000000005).endObject()
+ .field("location", "POINT(-73.570986000000005 45.509526999999999)")
.endObject())
.setRefreshPolicy(IMMEDIATE)
.get();
@@ -241,6 +264,13 @@ public void testCompleteLonRange() throws Exception {
.type("indexed")
).get();
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
+
+ // Distance query
+ searchResponse = client().prepareSearch()
+ .setQuery(
+ geoDistanceQuery("location").point(60.0, -20.0).distance(1800, DistanceUnit.MILES)
+ ).get();
+ assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
}
}
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryGeoPointIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryGeoPointIT.java
new file mode 100644
index 0000000000000..a1e5c95c30205
--- /dev/null
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryGeoPointIT.java
@@ -0,0 +1,37 @@
+/*
+ * 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.search.geo;
+
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+
+import java.io.IOException;
+
+public class GeoBoundingBoxQueryGeoPointIT extends AbstractGeoBoundingBoxQueryIT {
+
+ @Override
+ public XContentBuilder getMapping() throws IOException {
+ XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("_doc")
+ .startObject("properties").startObject("location").field("type", "geo_point");
+ xContentBuilder.endObject().endObject().endObject().endObject();
+ return xContentBuilder;
+ }
+}
+
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryGeoShapeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryGeoShapeIT.java
new file mode 100644
index 0000000000000..5c7c26249c51b
--- /dev/null
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryGeoShapeIT.java
@@ -0,0 +1,40 @@
+/*
+ * 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.search.geo;
+
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+
+import java.io.IOException;
+
+public class GeoBoundingBoxQueryGeoShapeIT extends AbstractGeoBoundingBoxQueryIT {
+
+ @Override
+ public XContentBuilder getMapping() throws IOException {
+ XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("_doc")
+ .startObject("properties").startObject("location").field("type", "geo_shape");
+ if (randomBoolean()) {
+ xContentBuilder.field("strategy", "recursive");
+ }
+ xContentBuilder.endObject().endObject().endObject().endObject();
+ return xContentBuilder;
+ }
+}
+
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 df2f6b6e4267b..76a2218da3966 100644
--- a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java
+++ b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java
@@ -19,9 +19,6 @@
package org.elasticsearch.index.query;
-import org.apache.lucene.document.LatLonDocValuesField;
-import org.apache.lucene.document.LatLonPoint;
-import org.apache.lucene.search.IndexOrDocValuesQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.ElasticsearchParseException;
@@ -31,13 +28,15 @@
import org.elasticsearch.common.geo.GeoBoundingBox;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
+import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.SpatialStrategy;
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.Rectangle;
import org.elasticsearch.geometry.utils.Geohash;
-import org.elasticsearch.index.mapper.GeoPointFieldMapper.GeoPointFieldType;
+import org.elasticsearch.index.mapper.GeoShapeQueryable;
import org.elasticsearch.index.mapper.MappedFieldType;
import java.io.IOException;
@@ -309,11 +308,12 @@ public Query doToQuery(QueryShardContext context) {
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
- throw new QueryShardException(context, "failed to find geo_point field [" + fieldName + "]");
+ throw new QueryShardException(context, "failed to find geo field [" + fieldName + "]");
}
}
- if (!(fieldType instanceof GeoPointFieldType)) {
- throw new QueryShardException(context, "field [" + fieldName + "] is not a geo_point field");
+ if (!(fieldType instanceof GeoShapeQueryable)) {
+ throw new QueryShardException(context,
+ "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "] for [" + NAME + "] query");
}
QueryValidationException exception = checkLatLon();
@@ -338,15 +338,10 @@ public Query doToQuery(QueryShardContext 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);
- }
- return query;
+ final GeoShapeQueryable geoShapeQueryable = (GeoShapeQueryable) fieldType;
+ final Rectangle rectangle =
+ new Rectangle(luceneTopLeft.getLon(), luceneBottomRight.getLon(), luceneTopLeft.getLat(), luceneBottomRight.getLat());
+ return geoShapeQueryable.geoShapeQuery(rectangle, fieldType.name(), SpatialStrategy.RECURSIVE, ShapeRelation.INTERSECTS, context);
}
@Override
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 f4c0da2c5256a..527e927299d51 100644
--- a/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java
+++ b/server/src/main/java/org/elasticsearch/index/query/GeoDistanceQueryBuilder.java
@@ -19,9 +19,6 @@
package org.elasticsearch.index.query;
-import org.apache.lucene.document.LatLonDocValuesField;
-import org.apache.lucene.document.LatLonPoint;
-import org.apache.lucene.search.IndexOrDocValuesQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.ParseField;
@@ -30,12 +27,15 @@
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
+import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.SpatialStrategy;
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.index.mapper.GeoPointFieldMapper.GeoPointFieldType;
+import org.elasticsearch.geometry.Circle;
+import org.elasticsearch.index.mapper.GeoShapeQueryable;
import org.elasticsearch.index.mapper.MappedFieldType;
import java.io.IOException;
@@ -232,12 +232,13 @@ protected Query doToQuery(QueryShardContext shardContext) throws IOException {
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
- throw new QueryShardException(shardContext, "failed to find geo_point field [" + fieldName + "]");
+ throw new QueryShardException(shardContext, "failed to find geo field [" + fieldName + "]");
}
}
- if (!(fieldType instanceof GeoPointFieldType)) {
- throw new QueryShardException(shardContext, "field [" + fieldName + "] is not a geo_point field");
+ if (!(fieldType instanceof GeoShapeQueryable)) {
+ throw new QueryShardException(shardContext,
+ "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "] for [" + NAME + "] query");
}
QueryValidationException exception = checkLatLon();
@@ -249,12 +250,11 @@ protected Query doToQuery(QueryShardContext shardContext) throws IOException {
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;
+ final GeoShapeQueryable geoShapeQueryable = (GeoShapeQueryable) fieldType;
+ final Circle circle =
+ new Circle(center.lon(), center.lat(), this.distance);
+ return geoShapeQueryable.geoShapeQuery(circle, fieldType.name(),
+ SpatialStrategy.RECURSIVE, ShapeRelation.INTERSECTS, shardContext);
}
@Override
diff --git a/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java
index c4fa6a3cc2371..a8ca282d7a64e 100644
--- a/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java
+++ b/server/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java
@@ -27,6 +27,8 @@
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
+import org.elasticsearch.index.mapper.GeoPointFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.test.AbstractQueryTestCase;
import org.elasticsearch.test.geo.RandomShapeGenerator;
@@ -45,7 +47,7 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase qb.toQuery(context));
- assertEquals("failed to find geo_point field [" + qb.fieldName() + "]", e.getMessage());
+ assertEquals("failed to find geo field [" + qb.fieldName() + "]", e.getMessage());
}
public void testBrokenCoordinateCannotBeSet() {
@@ -206,10 +208,11 @@ public void testStrictnessDefault() {
@Override
protected void doAssertLuceneQuery(GeoBoundingBoxQueryBuilder queryBuilder, Query query, QueryShardContext context)
throws IOException {
- MappedFieldType fieldType = context.getFieldType(queryBuilder.fieldName());
+ final MappedFieldType fieldType = context.getFieldType(queryBuilder.fieldName());
if (fieldType == null) {
assertTrue("Found no indexed geo query.", query instanceof MatchNoDocsQuery);
- } else if (query instanceof IndexOrDocValuesQuery) { // TODO: remove the if statement once we always use LatLonPoint
+ } else if (fieldType instanceof GeoPointFieldMapper.GeoPointFieldType) {
+ assertEquals(IndexOrDocValuesQuery.class, query.getClass());
Query indexQuery = ((IndexOrDocValuesQuery) query).getIndexQuery();
String expectedFieldName = expectedFieldName(queryBuilder.fieldName());
assertEquals(LatLonPoint.newBoxQuery(expectedFieldName,
@@ -223,6 +226,8 @@ protected void doAssertLuceneQuery(GeoBoundingBoxQueryBuilder queryBuilder, Quer
queryBuilder.topLeft().lat(),
queryBuilder.topLeft().lon(),
queryBuilder.bottomRight().lon()), dvQuery);
+ } else {
+ assertEquals(GeoShapeFieldMapper.GeoShapeFieldType.class, fieldType.getClass());
}
}
@@ -535,6 +540,6 @@ public void testIgnoreUnmapped() throws IOException {
final GeoBoundingBoxQueryBuilder failingQueryBuilder = new GeoBoundingBoxQueryBuilder("unmapped").setCorners(1.0, 0.0, 0.0, 1.0);
failingQueryBuilder.ignoreUnmapped(false);
QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(shardContext));
- assertThat(e.getMessage(), containsString("failed to find geo_point field [unmapped]"));
+ assertThat(e.getMessage(), containsString("failed to find geo field [unmapped]"));
}
}
diff --git a/server/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java
index dfb8211463ea5..8bba3a6967540 100644
--- a/server/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java
+++ b/server/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java
@@ -28,6 +28,9 @@
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
+import org.elasticsearch.index.mapper.GeoPointFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
+import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.test.AbstractQueryTestCase;
import org.elasticsearch.test.geo.RandomShapeGenerator;
import org.locationtech.spatial4j.shape.Point;
@@ -42,7 +45,7 @@ public class GeoDistanceQueryBuilderTests extends AbstractQueryTestCase failingQueryBuilder.toQuery(shardContext));
- assertThat(e.getMessage(), containsString("failed to find geo_point field [unmapped]"));
+ assertThat(e.getMessage(), containsString("failed to find geo field [unmapped]"));
}
public void testParseFailsWithMultipleFields() throws IOException {
diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/100_geo_point.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/100_geo_point.yml
index 6803b79c79ea7..328470c806de4 100644
--- a/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/100_geo_point.yml
+++ b/x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/100_geo_point.yml
@@ -86,6 +86,23 @@ setup:
---
"geo bounding box query":
+ - do:
+ search:
+ index: locations
+ body:
+ query:
+ geo_bounding_box:
+ location_from_source:
+ top_left:
+ lat: 10
+ lon: -10
+ bottom_right:
+ lat: -10
+ lon: 10
+ - match: {hits.total.value: 1}
+
+---
+"geo shape query":
- do:
search:
index: locations
@@ -98,6 +115,20 @@ setup:
coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
- match: {hits.total.value: 1}
+---
+"geo distance query":
+ - do:
+ search:
+ index: locations
+ body:
+ query:
+ geo_distance:
+ distance: "2000km"
+ location_from_source:
+ lat: 0
+ lon: 0
+ - match: {hits.total.value: 1}
+
---
"bounds agg":
- do: