Browse Source

Add support for Spatial Relationships to geo_point field (#67631)

Lucene 8.8 supports to query LatLonPoint field using spatial relationships.
Ignacio Vera 4 years ago
parent
commit
808b4e71f1
19 changed files with 505 additions and 686 deletions
  1. 23 41
      docs/reference/query-dsl/geo-shape-query.asciidoc
  2. 10 18
      server/src/main/java/org/elasticsearch/common/geo/GeoShapeUtils.java
  3. 14 5
      server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java
  4. 15 5
      server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java
  5. 0 188
      server/src/main/java/org/elasticsearch/index/query/VectorGeoPointShapeQueryProcessor.java
  6. 0 69
      server/src/main/java/org/elasticsearch/index/query/VectorGeoShapeQueryProcessor.java
  7. 0 140
      server/src/test/java/org/elasticsearch/search/geo/GeoPointShapeQueryTests.java
  8. 190 9
      server/src/test/java/org/elasticsearch/search/geo/GeoQueryTests.java
  9. 6 13
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/GeoPointScriptFieldType.java
  10. 93 13
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/GeoPointScriptFieldGeoShapeQuery.java
  11. 28 10
      x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/GeoPointScriptFieldGeoShapeQueryTests.java
  12. 20 4
      x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java
  13. 1 1
      x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/LatLonShapeDocValuesQuery.java
  14. 0 77
      x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/VectorGeoShapeWithDocValuesQueryProcessor.java
  15. 0 84
      x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/fielddata/GeometryDocValueTests.java
  16. 1 2
      x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/LatLonShapeDocValuesQueryTests.java
  17. 3 5
      x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/ingest/CircleProcessorTests.java
  18. 46 1
      x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/101_geo_point_from_source.yml
  19. 55 1
      x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/102_geo_point_source_in_query.yml

+ 23 - 41
docs/reference/query-dsl/geo-shape-query.asciidoc

@@ -6,7 +6,7 @@
 
 Filter documents indexed using the `geo_shape` or `geo_point` type.
 
-Requires the <<geo-shape,`geo_shape` Mapping>> or the 
+Requires the <<geo-shape,`geo_shape` Mapping>> or the
 <<geo-point,`geo_point` Mapping>>.
 
 The `geo_shape` query uses the same grid square representation as the
@@ -52,7 +52,7 @@ POST /example/_doc?refresh
 // TESTSETUP
 
 
-The following query will find the point using {es}'s `envelope` GeoJSON 
+The following query will find the point using {es}'s `envelope` GeoJSON
 extension:
 
 [source,console]
@@ -105,7 +105,7 @@ PUT /example_points/_doc/1?refresh
 // TEST[continued]
 
 
-Using the same query, the documents with matching `geo_point` fields are 
+Using the same query, the documents with matching `geo_point` fields are
 returned.
 
 [source,console]
@@ -170,16 +170,16 @@ GET /example_points/_search
 
 ==== Pre-Indexed Shape
 
-The query also supports using a shape which has already been indexed in another 
-index. This is particularly useful for when you have a pre-defined list of 
-shapes and you want to reference the list using 
-a logical name (for example 'New Zealand') rather than having to provide 
+The query also supports using a shape which has already been indexed in another
+index. This is particularly useful for when you have a pre-defined list of
+shapes and you want to reference the list using
+a logical name (for example 'New Zealand') rather than having to provide
 coordinates each time. In this situation, it is only necessary to provide:
 
 * `id` - The ID of the document that containing the pre-indexed shape.
-* `index` - Name of the index where the pre-indexed shape is. Defaults to 
+* `index` - Name of the index where the pre-indexed shape is. Defaults to
 'shapes'.
-* `path` - The field specified as path containing the pre-indexed shape. 
+* `path` - The field specified as path containing the pre-indexed shape.
 Defaults to 'shape'.
 * `routing` - The routing of the shape document if required.
 
@@ -230,27 +230,20 @@ GET /example/_search
 
 ==== Spatial Relations
 
-The <<spatial-strategy, geo_shape strategy>> mapping parameter determines which 
+The <<spatial-strategy, geo_shape strategy>> mapping parameter determines which
 spatial relation operators may be used at search time.
 
-The following is a complete list of spatial relation operators available when 
-searching a field of type `geo_shape`:
+The following is a complete list of spatial relation operators available when
+searching a geo field:
 
-* `INTERSECTS` - (default) Return all documents whose `geo_shape` field
+* `INTERSECTS` - (default) Return all documents whose `geo_shape` or `geo_point` field
 intersects the query geometry.
-* `DISJOINT` - Return all documents whose `geo_shape` field has nothing in 
+* `DISJOINT` - Return all documents whose `geo_shape` or `geo_point` field has nothing in
 common with the query geometry.
-* `WITHIN` - Return all documents whose `geo_shape` field is within the query 
+* `WITHIN` - Return all documents whose `geo_shape` or `geo_point` field is within the query
+geometry. Line geometries are not supported.
+* `CONTAINS` - Return all documents whose `geo_shape` or `geo_point` field contains the query
 geometry.
-* `CONTAINS` - Return all documents whose `geo_shape` field contains the query 
-geometry.
-
-When searching a field of type `geo_point` there is a single supported spatial 
-relation operator:
-
-* `INTERSECTS` - (default) Return all documents whose `geo_point` field 
-intersects the query geometry.
-
 
 [discrete]
 ==== Ignore Unmapped
@@ -261,28 +254,17 @@ querying multiple indexes which might have different mappings. When set to
 `false` (the default value) the query will throw an exception if the field
 is not mapped.
 
-
-==== Shape Types supported for Geo-Point
-
-When searching a field of type `geo_point` the following shape types are not 
-supported:
-
-* `POINT`
-* `LINE`
-* `MULTIPOINT`
-* `MULTILINE`
-
 [[geo-shape-query-notes]]
 ==== Notes
 
-* Geo-shape queries on geo-shapes implemented with 
-  <<prefix-trees, `PrefixTrees`>> will not be executed if 
-  <<query-dsl-allow-expensive-queries, `search.allow_expensive_queries`>> is set 
+* Geo-shape queries on geo-shapes implemented with
+  <<prefix-trees, `PrefixTrees`>> will not be executed if
+  <<query-dsl-allow-expensive-queries, `search.allow_expensive_queries`>> is set
   to false.
 
 
-* When data is indexed in a `geo_shape` field as an array of shapes, the arrays 
-  are treated as one shape. For this reason, the following requests are 
+* When data is indexed in a `geo_shape` field as an array of shapes, the arrays
+  are treated as one shape. For this reason, the following requests are
   equivalent.
 
 [source,console]
@@ -307,7 +289,7 @@ PUT /test/_doc/1
 --------------------------------------------------
 PUT /test/_doc/1
 {
-  "location": 
+  "location":
     {
       "coordinates": [[46.25,20.14],[47.49,19.04]],
       "type": "multipoint"

+ 10 - 18
server/src/main/java/org/elasticsearch/common/geo/GeoShapeUtils.java

@@ -81,13 +81,15 @@ public class GeoShapeUtils {
         String name,
         SearchExecutionContext context,
         Geometry geometry,
-        List<Class<? extends Geometry>> unsupportedGeometries
+        ShapeRelation relation
     ) {
+        if (geometry == null) {
+            return new LatLonGeometry[0];
+        }
         final List<LatLonGeometry> geometries = new ArrayList<>();
         geometry.visit(new GeometryVisitor<>() {
             @Override
             public Void visit(Circle circle) {
-                checkSupported(circle);
                 if (circle.isEmpty() == false) {
                     geometries.add(GeoShapeUtils.toLuceneCircle(circle));
                 }
@@ -96,7 +98,6 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(GeometryCollection<?> collection) {
-                checkSupported(collection);
                 if (collection.isEmpty() == false) {
                     for (Geometry shape : collection) {
                         shape.visit(this);
@@ -107,8 +108,12 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(org.elasticsearch.geometry.Line line) {
-                checkSupported(line);
                 if (line.isEmpty() == false) {
+                    if (relation == ShapeRelation.WITHIN) {
+                        // Line geometries and WITHIN relation is not supported by Lucene. Throw an error here
+                        // to have same behavior for runtime fields.
+                        throw new QueryShardException(context, "Field [" + name + "] found an unsupported shape Line");
+                    }
                     geometries.add(GeoShapeUtils.toLuceneLine(line));
                 }
                 return null;
@@ -116,12 +121,11 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(LinearRing ring) {
-                throw new QueryShardException(context, "Field [" + name + "] found and unsupported shape LinearRing");
+                throw new QueryShardException(context, "Field [" + name + "] found an unsupported shape LinearRing");
             }
 
             @Override
             public Void visit(MultiLine multiLine) {
-                checkSupported(multiLine);
                 if (multiLine.isEmpty() == false) {
                     for (Line line : multiLine) {
                         visit(line);
@@ -132,7 +136,6 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(MultiPoint multiPoint) {
-                checkSupported(multiPoint);
                 if (multiPoint.isEmpty() == false) {
                     for (Point point : multiPoint) {
                         visit(point);
@@ -143,7 +146,6 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(MultiPolygon multiPolygon) {
-                checkSupported(multiPolygon);
                 if (multiPolygon.isEmpty() == false) {
                     for (Polygon polygon : multiPolygon) {
                         visit(polygon);
@@ -154,7 +156,6 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(Point point) {
-                checkSupported(point);
                 if (point.isEmpty() == false) {
                     geometries.add(toLucenePoint(point));
                 }
@@ -164,7 +165,6 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(org.elasticsearch.geometry.Polygon polygon) {
-                checkSupported(polygon);
                 if (polygon.isEmpty() == false) {
                     List<org.elasticsearch.geometry.Polygon> collector = new ArrayList<>();
                     GeoPolygonDecomposer.decomposePolygon(polygon, true, collector);
@@ -175,23 +175,15 @@ public class GeoShapeUtils {
 
             @Override
             public Void visit(Rectangle r) {
-                checkSupported(r);
                 if (r.isEmpty() == false) {
                     geometries.add(toLuceneRectangle(r));
                 }
                 return null;
             }
-
-            private void checkSupported(Geometry geometry) {
-                if (unsupportedGeometries.contains(geometry.getClass())) {
-                    throw new QueryShardException(context, "Field [" + name + "] found and unsupported shape [" + geometry.type() + "]");
-                }
-            }
         });
         return geometries.toArray(new LatLonGeometry[geometries.size()]);
     }
 
     private GeoShapeUtils() {
     }
-
 }

+ 14 - 5
server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java

@@ -21,11 +21,15 @@ package org.elasticsearch.index.mapper;
 import org.apache.lucene.document.LatLonDocValuesField;
 import org.apache.lucene.document.LatLonPoint;
 import org.apache.lucene.document.StoredField;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.search.IndexOrDocValuesQuery;
+import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.Query;
 import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.common.Explicit;
 import org.elasticsearch.common.geo.GeoPoint;
+import org.elasticsearch.common.geo.GeoShapeUtils;
 import org.elasticsearch.common.geo.GeoUtils;
 import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.common.unit.DistanceUnit;
@@ -35,7 +39,6 @@ import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointIndexFieldData;
 import org.elasticsearch.index.mapper.GeoPointFieldMapper.ParsedGeoPoint;
 import org.elasticsearch.index.query.SearchExecutionContext;
-import org.elasticsearch.index.query.VectorGeoPointShapeQueryProcessor;
 import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
 import org.elasticsearch.search.lookup.SearchLookup;
 
@@ -185,12 +188,9 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
 
     public static class GeoPointFieldType extends AbstractGeometryFieldType implements GeoShapeQueryable {
 
-        private final VectorGeoPointShapeQueryProcessor queryProcessor;
-
         private GeoPointFieldType(String name, boolean indexed, boolean stored, boolean hasDocValues,
                                   Parser<?> parser, Map<String, String> meta) {
             super(name, indexed, stored, hasDocValues, true, parser, meta);
-            this.queryProcessor = new VectorGeoPointShapeQueryProcessor();
         }
 
         public GeoPointFieldType(String name) {
@@ -204,7 +204,16 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
 
         @Override
         public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-            return queryProcessor.geoShapeQuery(shape, fieldName, relation, context);
+            final LatLonGeometry[] luceneGeometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, relation);
+            if (luceneGeometries.length == 0) {
+                return new MatchNoDocsQuery();
+            }
+            Query query = LatLonPoint.newGeometryQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
+            if (hasDocValues()) {
+                Query dvQuery = LatLonDocValuesField.newSlowGeometryQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
+                query = new IndexOrDocValuesQuery(query, dvQuery);
+            }
+            return query;
         }
 
         @Override

+ 15 - 5
server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java

@@ -19,15 +19,19 @@
 package org.elasticsearch.index.mapper;
 
 import org.apache.lucene.document.LatLonShape;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.Query;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.Explicit;
+import org.elasticsearch.common.geo.GeoShapeUtils;
 import org.elasticsearch.common.geo.GeometryParser;
 import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
 import org.elasticsearch.geometry.Geometry;
 import org.elasticsearch.index.query.SearchExecutionContext;
-import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
+import org.elasticsearch.index.query.QueryShardException;
 
 import java.util.Arrays;
 import java.util.List;
@@ -109,12 +113,9 @@ public class GeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geomet
 
     public static class GeoShapeFieldType extends AbstractShapeGeometryFieldType implements GeoShapeQueryable {
 
-        private final VectorGeoShapeQueryProcessor queryProcessor;
-
         public GeoShapeFieldType(String name, boolean indexed, Orientation orientation,
                                  Parser<Geometry> parser, Map<String, String> meta) {
             super(name, indexed, false, false, false, parser, orientation, meta);
-            this.queryProcessor = new VectorGeoShapeQueryProcessor();
         }
 
         @Override
@@ -124,7 +125,16 @@ public class GeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geomet
 
         @Override
         public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-            return queryProcessor.geoShapeQuery(shape, fieldName, relation, context);
+            // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0)
+            if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(Version.V_7_5_0)) {
+                throw new QueryShardException(context,
+                    ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "].");
+            }
+            final LatLonGeometry[] luceneGeometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, relation);
+            if (luceneGeometries.length == 0) {
+                return new MatchNoDocsQuery();
+            }
+            return LatLonShape.newGeometryQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
         }
     }
 

+ 0 - 188
server/src/main/java/org/elasticsearch/index/query/VectorGeoPointShapeQueryProcessor.java

@@ -1,188 +0,0 @@
-/*
- * 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.apache.lucene.document.LatLonDocValuesField;
-import org.apache.lucene.document.LatLonPoint;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.IndexOrDocValuesQuery;
-import org.apache.lucene.search.Query;
-import org.elasticsearch.common.geo.GeoPolygonDecomposer;
-import org.elasticsearch.common.geo.GeoShapeType;
-import org.elasticsearch.common.geo.GeoShapeUtils;
-import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.geometry.Circle;
-import org.elasticsearch.geometry.Geometry;
-import org.elasticsearch.geometry.GeometryCollection;
-import org.elasticsearch.geometry.GeometryVisitor;
-import org.elasticsearch.geometry.LinearRing;
-import org.elasticsearch.geometry.MultiLine;
-import org.elasticsearch.geometry.MultiPoint;
-import org.elasticsearch.geometry.MultiPolygon;
-import org.elasticsearch.geometry.Point;
-import org.elasticsearch.geometry.Polygon;
-import org.elasticsearch.geometry.Rectangle;
-import org.elasticsearch.geometry.ShapeType;
-import org.elasticsearch.index.mapper.GeoPointFieldMapper;
-import org.elasticsearch.index.mapper.MappedFieldType;
-
-import java.util.ArrayList;
-
-public class VectorGeoPointShapeQueryProcessor {
-
-    public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-        validateIsGeoPointFieldType(fieldName, context);
-        // geo points only support intersects
-        if (relation != ShapeRelation.INTERSECTS) {
-            throw new QueryShardException(context,
-                relation+ " query relation not supported for Field [" + fieldName + "].");
-        }
-        // wrap geoQuery as a ConstantScoreQuery
-        return getVectorQueryFromShape(shape, fieldName, relation, context);
-    }
-
-    private void validateIsGeoPointFieldType(String fieldName, SearchExecutionContext context) {
-        MappedFieldType fieldType = context.getFieldType(fieldName);
-        if (fieldType instanceof GeoPointFieldMapper.GeoPointFieldType == false) {
-            throw new QueryShardException(context, "Expected " + GeoPointFieldMapper.CONTENT_TYPE
-                + " field type for Field [" + fieldName + "] but found " + fieldType.typeName());
-        }
-    }
-
-    protected Query getVectorQueryFromShape(
-        Geometry queryShape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-        ShapeVisitor shapeVisitor = new ShapeVisitor(context, fieldName, relation);
-        return queryShape.visit(shapeVisitor);
-    }
-
-    private class ShapeVisitor implements GeometryVisitor<Query, RuntimeException> {
-        SearchExecutionContext context;
-        MappedFieldType fieldType;
-        String fieldName;
-        ShapeRelation relation;
-
-        ShapeVisitor(SearchExecutionContext context, String fieldName, ShapeRelation relation) {
-            this.context = context;
-            this.fieldType = context.getFieldType(fieldName);
-            this.fieldName = fieldName;
-            this.relation = relation;
-        }
-
-        @Override
-        public Query visit(Circle circle) {
-            Query query = LatLonPoint.newDistanceQuery(
-                fieldName, circle.getLat(), circle.getLon(), circle.getRadiusMeters());
-            if (fieldType.hasDocValues()) {
-                Query dvQuery = LatLonDocValuesField.newSlowDistanceQuery(
-                    fieldName, circle.getLat(), circle.getLon(), circle.getRadiusMeters());
-                query = new IndexOrDocValuesQuery(query, dvQuery);
-            }
-            return query;
-        }
-
-        @Override
-        public Query visit(GeometryCollection<?> collection) {
-            BooleanQuery.Builder bqb = new BooleanQuery.Builder();
-            visit(bqb, collection);
-            return bqb.build();
-        }
-
-        private void visit(BooleanQuery.Builder bqb, GeometryCollection<?> collection) {
-            BooleanClause.Occur occur = BooleanClause.Occur.FILTER;
-            for (Geometry shape : collection) {
-                bqb.add(shape.visit(this), occur);
-            }
-        }
-
-        @Override
-        public Query visit(org.elasticsearch.geometry.Line line) {
-            throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
-                + GeoShapeType.LINESTRING + " queries");
-        }
-
-        @Override
-        // don't think this is called directly
-        public Query visit(LinearRing ring) {
-            throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
-                + ShapeType.LINEARRING + " queries");
-        }
-
-        @Override
-        public Query visit(MultiLine multiLine) {
-            throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
-                + GeoShapeType.MULTILINESTRING + " queries");
-        }
-
-        @Override
-        public Query visit(MultiPoint multiPoint) {
-            throw new QueryShardException(context, "Field [" + fieldName + "] does not support "
-                + GeoShapeType.MULTIPOINT + " queries");
-        }
-
-        // helper for visit(MultiPolygon multiPolygon) and visit(Polygon polygon)
-        private Query visit(ArrayList<Polygon> collector) {
-            org.apache.lucene.geo.Polygon[] lucenePolygons =
-                new org.apache.lucene.geo.Polygon[collector.size()];
-            for (int i = 0; i < collector.size(); i++) {
-                lucenePolygons[i] = GeoShapeUtils.toLucenePolygon(collector.get(i));
-            }
-            Query query = LatLonPoint.newPolygonQuery(fieldName, lucenePolygons);
-            if (fieldType.hasDocValues()) {
-                Query dvQuery = LatLonDocValuesField.newSlowPolygonQuery(fieldName, lucenePolygons);
-                query = new IndexOrDocValuesQuery(query, dvQuery);
-            }
-            return query;
-        }
-
-        @Override
-        public Query visit(MultiPolygon multiPolygon) {
-            ArrayList<org.elasticsearch.geometry.Polygon> collector = new ArrayList<>();
-            GeoPolygonDecomposer.decomposeMultiPolygon(multiPolygon, true, collector);
-            return visit(collector);
-        }
-
-        @Override
-        public Query visit(Point point) {
-            // not currently supported
-            throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.POINT +
-                " queries");
-        }
-
-        @Override
-        public Query visit(Polygon polygon) {
-            ArrayList<org.elasticsearch.geometry.Polygon> collector = new ArrayList<>();
-            GeoPolygonDecomposer.decomposePolygon(polygon, true, collector);
-            return visit(collector);
-        }
-
-        @Override
-        public Query visit(Rectangle r) {
-            Query query = LatLonPoint.newBoxQuery(fieldName, r.getMinY(), r.getMaxY(), r.getMinX(), r.getMaxX());
-            if (fieldType.hasDocValues()) {
-                Query dvQuery = LatLonDocValuesField.newSlowBoxQuery(
-                    fieldName, r.getMinY(), r.getMaxY(), r.getMinX(), r.getMaxX());
-                query = new IndexOrDocValuesQuery(query, dvQuery);
-            }
-            return query;
-        }
-    }
-}
-

+ 0 - 69
server/src/main/java/org/elasticsearch/index/query/VectorGeoShapeQueryProcessor.java

@@ -1,69 +0,0 @@
-/*
- * 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.apache.lucene.document.LatLonShape;
-import org.apache.lucene.geo.LatLonGeometry;
-import org.apache.lucene.search.MatchNoDocsQuery;
-import org.apache.lucene.search.Query;
-import org.elasticsearch.Version;
-import org.elasticsearch.common.geo.GeoShapeUtils;
-import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.geometry.Geometry;
-import org.elasticsearch.geometry.Line;
-import org.elasticsearch.geometry.MultiLine;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-public class VectorGeoShapeQueryProcessor {
-
-    private static final List<Class<? extends Geometry>> WITHIN_UNSUPPORTED_GEOMETRIES = new ArrayList<>();
-    static {
-        WITHIN_UNSUPPORTED_GEOMETRIES.add(Line.class);
-        WITHIN_UNSUPPORTED_GEOMETRIES.add(MultiLine.class);
-    }
-
-    public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-        // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0)
-        if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(Version.V_7_5_0)) {
-            throw new QueryShardException(context,
-                ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "].");
-        }
-        // wrap geoQuery as a ConstantScoreQuery
-        return getVectorQueryFromShape(shape, fieldName, relation, context);
-    }
-
-    private Query getVectorQueryFromShape(Geometry queryShape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-        final LatLonGeometry[] luceneGeometries;
-        if (relation == ShapeRelation.WITHIN) {
-            luceneGeometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, queryShape, WITHIN_UNSUPPORTED_GEOMETRIES);
-        } else {
-            luceneGeometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, queryShape, Collections.emptyList());
-        }
-        if (luceneGeometries.length == 0) {
-            return new MatchNoDocsQuery();
-        }
-        return LatLonShape.newGeometryQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
-    }
-}
-

+ 0 - 140
server/src/test/java/org/elasticsearch/search/geo/GeoPointShapeQueryTests.java

@@ -19,29 +19,8 @@
 
 package org.elasticsearch.search.geo;
 
-import org.elasticsearch.action.search.SearchAction;
-import org.elasticsearch.action.search.SearchPhaseExecutionException;
-import org.elasticsearch.action.search.SearchRequestBuilder;
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.geo.GeoShapeType;
-import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
-import org.elasticsearch.common.geo.builders.LineStringBuilder;
-import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
-import org.elasticsearch.common.geo.builders.MultiPointBuilder;
-import org.elasticsearch.common.geo.builders.PointBuilder;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.geometry.Line;
-import org.elasticsearch.geometry.LinearRing;
-import org.elasticsearch.geometry.MultiLine;
-import org.elasticsearch.geometry.MultiPoint;
-import org.elasticsearch.geometry.Point;
-import org.elasticsearch.geometry.Rectangle;
-import org.elasticsearch.index.query.GeoShapeQueryBuilder;
-import org.elasticsearch.index.query.QueryBuilders;
-
-import static org.hamcrest.Matchers.containsString;
 
 public class GeoPointShapeQueryTests extends GeoQueryTests {
 
@@ -54,123 +33,4 @@ public class GeoPointShapeQueryTests extends GeoQueryTests {
 
         return xcb;
     }
-
-    public void testProcessRelationSupport() throws Exception {
-        String mapping = Strings.toString(createDefaultMapping());
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
-        ensureGreen();
-
-        Rectangle rectangle = new Rectangle(-35, -25, -25, -35);
-
-        for (ShapeRelation shapeRelation : ShapeRelation.values()) {
-            if (!shapeRelation.equals(ShapeRelation.INTERSECTS)) {
-                SearchPhaseExecutionException e = expectThrows(SearchPhaseExecutionException.class, () ->
-                    client().prepareSearch("test")
-                        .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, rectangle)
-                            .relation(shapeRelation))
-                        .get());
-                assertThat(e.getCause().getMessage(),
-                    containsString(shapeRelation
-                        + " query relation not supported for Field [" + defaultGeoFieldName + "]"));
-            }
-        }
-    }
-
-    public void testQueryLine() throws Exception {
-        String mapping = Strings.toString(createDefaultMapping());
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
-        ensureGreen();
-
-        Line line = new Line(new double[]{-25, -25}, new double[]{-35, -35});
-
-        try {
-            client().prepareSearch("test")
-                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, line)).get();
-        } catch (
-            SearchPhaseExecutionException e) {
-            assertThat(e.getCause().getMessage(),
-                containsString("does not support " + GeoShapeType.LINESTRING + " queries"));
-        }
-    }
-
-    public void testQueryLinearRing() throws Exception {
-        String mapping = Strings.toString(createDefaultMapping());
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
-        ensureGreen();
-
-        LinearRing linearRing = new LinearRing(new double[]{-25,-35,-25}, new double[]{-25,-35,-25});
-
-        try {
-            // LinearRing extends Line implements Geometry: expose the build process
-            GeoShapeQueryBuilder queryBuilder = new GeoShapeQueryBuilder(defaultGeoFieldName, linearRing);
-            SearchRequestBuilder searchRequestBuilder = new SearchRequestBuilder(client(), SearchAction.INSTANCE);
-            searchRequestBuilder.setQuery(queryBuilder);
-            searchRequestBuilder.setIndices("test");
-            searchRequestBuilder.get();
-        } catch (
-            SearchPhaseExecutionException e) {
-            assertThat(e.getCause().getMessage(),
-                containsString("Field [" + defaultGeoFieldName + "] does not support LINEARRING queries"));
-        }
-    }
-
-    public void testQueryMultiLine() throws Exception {
-        String mapping = Strings.toString(createDefaultMapping());
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
-        ensureGreen();
-
-        CoordinatesBuilder coords1 = new CoordinatesBuilder()
-            .coordinate(-35,-35)
-            .coordinate(-25,-25);
-        CoordinatesBuilder coords2 = new CoordinatesBuilder()
-            .coordinate(-15,-15)
-            .coordinate(-5,-5);
-        LineStringBuilder lsb1 = new LineStringBuilder(coords1);
-        LineStringBuilder lsb2 = new LineStringBuilder(coords2);
-        MultiLineStringBuilder mlb = new MultiLineStringBuilder().linestring(lsb1).linestring(lsb2);
-        MultiLine multiline = (MultiLine) mlb.buildGeometry();
-
-        try {
-            client().prepareSearch("test")
-                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiline)).get();
-        } catch (Exception e) {
-            assertThat(e.getCause().getMessage(),
-                containsString("does not support " + GeoShapeType.MULTILINESTRING + " queries"));
-        }
-    }
-
-    public void testQueryMultiPoint() throws Exception {
-        String mapping = Strings.toString(createDefaultMapping());
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
-        ensureGreen();
-
-        MultiPointBuilder mpb = new MultiPointBuilder().coordinate(-35,-25).coordinate(-15,-5);
-        MultiPoint multiPoint = mpb.buildGeometry();
-
-        try {
-            client().prepareSearch("test")
-                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiPoint)).get();
-        } catch (Exception e) {
-            assertThat(e.getCause().getMessage(),
-                containsString("does not support " + GeoShapeType.MULTIPOINT + " queries"));
-        }
-    }
-
-    public void testQueryPoint() throws Exception {
-        String mapping = Strings.toString(createDefaultMapping());
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
-        ensureGreen();
-
-        PointBuilder pb = new PointBuilder().coordinate(-35, -25);
-        Point point = pb.buildGeometry();
-
-        try {
-            client().prepareSearch("test")
-                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, point)).get();
-        } catch (Exception e) {
-            assertThat(e.getCause().getMessage(),
-                containsString("does not support " + GeoShapeType.POINT + " queries"));
-        }
-    }
-
 }

+ 190 - 9
server/src/test/java/org/elasticsearch/search/geo/GeoQueryTests.java

@@ -20,6 +20,9 @@
 package org.elasticsearch.search.geo;
 
 import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.search.SearchAction;
+import org.elasticsearch.action.search.SearchPhaseExecutionException;
+import org.elasticsearch.action.search.SearchRequestBuilder;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.geo.GeoShapeType;
@@ -28,12 +31,21 @@ import org.elasticsearch.common.geo.builders.CircleBuilder;
 import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
 import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
 import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
+import org.elasticsearch.common.geo.builders.LineStringBuilder;
+import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
+import org.elasticsearch.common.geo.builders.MultiPointBuilder;
 import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
+import org.elasticsearch.common.geo.builders.PointBuilder;
 import org.elasticsearch.common.geo.builders.PolygonBuilder;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.geometry.Geometry;
+import org.elasticsearch.geometry.Line;
+import org.elasticsearch.geometry.LinearRing;
+import org.elasticsearch.geometry.MultiLine;
+import org.elasticsearch.geometry.MultiPoint;
+import org.elasticsearch.geometry.Point;
 import org.elasticsearch.geometry.Rectangle;
 import org.elasticsearch.index.query.GeoShapeQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
@@ -232,16 +244,51 @@ public abstract class GeoQueryTests extends ESSingleNodeTestCase {
 
         GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(mp);
         Geometry geometry = builder.buildGeometry();
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
-                .relation(ShapeRelation.INTERSECTS))
-            .get();
+        {
+            SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
+                    .relation(ShapeRelation.INTERSECTS))
+                .get();
 
-        assertSearchResponse(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(2));
-        assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2")));
-        assertThat(searchResponse.getHits().getAt(1).getId(), not(equalTo("2")));
+            assertSearchResponse(searchResponse);
+            assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
+            assertThat(searchResponse.getHits().getHits().length, equalTo(2));
+            assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2")));
+            assertThat(searchResponse.getHits().getAt(1).getId(), not(equalTo("2")));
+        }
+        {
+            SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
+                    .relation(ShapeRelation.WITHIN))
+                .get();
+
+            assertSearchResponse(searchResponse);
+            assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
+            assertThat(searchResponse.getHits().getHits().length, equalTo(2));
+            assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2")));
+            assertThat(searchResponse.getHits().getAt(1).getId(), not(equalTo("2")));
+        }
+        {
+            SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
+                    .relation(ShapeRelation.DISJOINT))
+                .get();
+
+            assertSearchResponse(searchResponse);
+            assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+            assertThat(searchResponse.getHits().getHits().length, equalTo(1));
+            assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2"));
+        }
+        {
+            SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
+                    .relation(ShapeRelation.CONTAINS))
+                .get();
+
+            assertSearchResponse(searchResponse);
+            assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L));
+            assertThat(searchResponse.getHits().getHits().length, equalTo(0));
+        }
     }
 
     public void testIndexPointsRectangle() throws Exception {
@@ -450,4 +497,138 @@ public abstract class GeoQueryTests extends ESSingleNodeTestCase {
         assertNotEquals("3", searchHits.getAt(0).getId());
         assertNotEquals("3", searchHits.getAt(1).getId());
     }
+
+    public void testWithInQueryLine() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        Line line = new Line(new double[]{-25, -25}, new double[]{-35, -35});
+
+        try {
+            client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, line).relation(ShapeRelation.WITHIN)).get();
+        } catch (
+            SearchPhaseExecutionException e) {
+            assertThat(e.getCause().getMessage(),
+                containsString("Field [geo] found an unsupported shape Line"));
+        }
+    }
+
+    public void testQueryWithinMultiLine() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        CoordinatesBuilder coords1 = new CoordinatesBuilder()
+            .coordinate(-35,-35)
+            .coordinate(-25,-25);
+        CoordinatesBuilder coords2 = new CoordinatesBuilder()
+            .coordinate(-15,-15)
+            .coordinate(-5,-5);
+        LineStringBuilder lsb1 = new LineStringBuilder(coords1);
+        LineStringBuilder lsb2 = new LineStringBuilder(coords2);
+        MultiLineStringBuilder mlb = new MultiLineStringBuilder().linestring(lsb1).linestring(lsb2);
+        MultiLine multiline = (MultiLine) mlb.buildGeometry();
+
+        GeoShapeQueryBuilder builder = QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiline).relation(ShapeRelation.WITHIN);
+        SearchRequestBuilder searchRequestBuilder = client().prepareSearch("test").setQuery(builder);
+        SearchPhaseExecutionException e = expectThrows(SearchPhaseExecutionException.class, searchRequestBuilder::get);
+        assertThat(e.getCause().getMessage(),
+            containsString("Field [" + defaultGeoFieldName + "] found an unsupported shape Line"));
+    }
+
+    public void testQueryLinearRing() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        LinearRing linearRing = new LinearRing(new double[]{-25, -35, -25}, new double[]{-25, -35, -25});
+
+        // LinearRing extends Line implements Geometry: expose the build process
+        GeoShapeQueryBuilder queryBuilder = new GeoShapeQueryBuilder(defaultGeoFieldName, linearRing);
+        SearchRequestBuilder searchRequestBuilder = new SearchRequestBuilder(client(), SearchAction.INSTANCE);
+        searchRequestBuilder.setQuery(queryBuilder);
+        searchRequestBuilder.setIndices("test");
+        SearchPhaseExecutionException e = expectThrows(SearchPhaseExecutionException.class, searchRequestBuilder::get);
+        assertThat(e.getCause().getMessage(),
+            containsString("Field [" + defaultGeoFieldName + "] found an unsupported shape LinearRing"));
+    }
+
+    public void testQueryPoint() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex("test").setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-35 -25)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        PointBuilder pb = new PointBuilder().coordinate(-35, -25);
+        Point point = pb.buildGeometry();
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, point)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(1, searchHits.getTotalHits().value);
+        }
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, point).relation(ShapeRelation.WITHIN)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(1, searchHits.getTotalHits().value);
+        }
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, point).relation(ShapeRelation.CONTAINS)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(1, searchHits.getTotalHits().value);
+        }
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, point).relation(ShapeRelation.DISJOINT)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(0, searchHits.getTotalHits().value);
+        }
+    }
+
+    public void testQueryMultiPoint() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex("test").setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-35 -25)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        MultiPointBuilder mpb = new MultiPointBuilder().coordinate(-35,-25).coordinate(-15,-5);
+        MultiPoint multiPoint = mpb.buildGeometry();
+
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiPoint)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(1, searchHits.getTotalHits().value);
+        }
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiPoint).relation(ShapeRelation.WITHIN)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(1, searchHits.getTotalHits().value);
+        }
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiPoint).relation(ShapeRelation.CONTAINS)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(0, searchHits.getTotalHits().value);
+        }
+        {
+            SearchResponse response = client().prepareSearch("test")
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiPoint).relation(ShapeRelation.DISJOINT)).get();
+            SearchHits searchHits = response.getHits();
+            assertEquals(0, searchHits.getTotalHits().value);
+        }
+    }
 }

+ 6 - 13
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/GeoPointScriptFieldType.java

@@ -7,6 +7,7 @@
 package org.elasticsearch.xpack.runtimefields.mapper;
 
 import org.apache.lucene.geo.LatLonGeometry;
+import org.apache.lucene.geo.Point;
 import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.Query;
 import org.elasticsearch.common.CheckedBiConsumer;
@@ -15,8 +16,6 @@ import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.common.time.DateMathParser;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.geometry.Geometry;
-import org.elasticsearch.geometry.Line;
-import org.elasticsearch.geometry.MultiLine;
 import org.elasticsearch.index.mapper.GeoPointFieldMapper;
 import org.elasticsearch.index.mapper.GeoShapeQueryable;
 import org.elasticsearch.index.mapper.RuntimeFieldType;
@@ -29,8 +28,7 @@ import org.elasticsearch.xpack.runtimefields.query.GeoPointScriptFieldGeoShapeQu
 
 import java.io.IOException;
 import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
 import java.util.Map;
 import java.util.function.Supplier;
 
@@ -47,12 +45,6 @@ public final class GeoPointScriptFieldType extends AbstractScriptFieldType<GeoPo
         }
     });
 
-    private static final List<Class<? extends Geometry>> UNSUPPORTED_GEOMETRIES = new ArrayList<>();
-    static {
-        UNSUPPORTED_GEOMETRIES.add(Line.class);
-        UNSUPPORTED_GEOMETRIES.add(MultiLine.class);
-    }
-
     private GeoPointScriptFieldType(String name, GeoPointFieldScript.Factory scriptFactory, Builder builder) {
         super(name, scriptFactory::newFactory, builder);
     }
@@ -105,10 +97,11 @@ public final class GeoPointScriptFieldType extends AbstractScriptFieldType<GeoPo
 
     @Override
     public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-        if (shape == null) {
+        final LatLonGeometry[] luceneGeometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, relation);
+        if (luceneGeometries.length == 0
+            || (relation == ShapeRelation.CONTAINS && Arrays.stream(luceneGeometries).anyMatch(g -> (g instanceof Point) == false))) {
             return new MatchNoDocsQuery();
         }
-        final LatLonGeometry[] geometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, UNSUPPORTED_GEOMETRIES);
-        return new GeoPointScriptFieldGeoShapeQuery(script, leafFactory(context), fieldName, geometries);
+        return new GeoPointScriptFieldGeoShapeQuery(script, leafFactory(context), fieldName, relation, luceneGeometries);
     }
 }

+ 93 - 13
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/query/GeoPointScriptFieldGeoShapeQuery.java

@@ -6,8 +6,10 @@
 
 package org.elasticsearch.xpack.runtimefields.query;
 
+import org.apache.lucene.geo.Component2D;
 import org.apache.lucene.geo.GeoEncodingUtils;
 import org.apache.lucene.geo.LatLonGeometry;
+import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.xpack.runtimefields.mapper.GeoPointFieldScript;
 
@@ -16,31 +18,26 @@ import java.util.Objects;
 
 public class GeoPointScriptFieldGeoShapeQuery extends AbstractGeoPointScriptFieldQuery {
 
-    private final GeoEncodingUtils.Component2DPredicate predicate;
+    private final SpatialPredicate predicate;
     private final LatLonGeometry[] geometries;
+    private final ShapeRelation relation;
 
     public GeoPointScriptFieldGeoShapeQuery(
         Script script,
         GeoPointFieldScript.LeafFactory leafFactory,
         String fieldName,
+        ShapeRelation relation,
         LatLonGeometry... geometries
     ) {
         super(script, leafFactory, fieldName);
         this.geometries = geometries;
-        predicate = GeoEncodingUtils.createComponentPredicate(LatLonGeometry.create(geometries));
+        this.relation = relation;
+        this.predicate = getPredicate(relation, geometries);
     }
 
     @Override
     protected boolean matches(long[] values, int count) {
-        for (int i = 0; i < count; i++) {
-            final long value = values[i];
-            final int lat = (int) (value >>> 32);
-            final int lon = (int) (value & 0xFFFFFFFF);
-            if (predicate.test(lat, lon)) {
-                return true;
-            }
-        }
-        return false;
+        return predicate.matches(values, count);
     }
 
     @Override
@@ -57,11 +54,94 @@ public class GeoPointScriptFieldGeoShapeQuery extends AbstractGeoPointScriptFiel
         if (o == null || getClass() != o.getClass()) return false;
         if (!super.equals(o)) return false;
         GeoPointScriptFieldGeoShapeQuery that = (GeoPointScriptFieldGeoShapeQuery) o;
-        return Arrays.equals(geometries, that.geometries);
+        return relation == that.relation && Arrays.equals(geometries, that.geometries);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), Arrays.hashCode(geometries));
+        return Objects.hash(super.hashCode(), relation, Arrays.hashCode(geometries));
+    }
+
+    @FunctionalInterface
+    private interface SpatialPredicate {
+        boolean matches(long[] values, int count);
+    }
+
+    private static SpatialPredicate getPredicate(ShapeRelation relation, LatLonGeometry... geometries) {
+        switch (relation) {
+            case INTERSECTS: {
+                final GeoEncodingUtils.Component2DPredicate predicate = GeoEncodingUtils.createComponentPredicate(
+                    LatLonGeometry.create(geometries)
+                );
+                return (values, count) -> {
+                    for (int i = 0; i < count; i++) {
+                        final long value = values[i];
+                        final int lat = (int) (value >>> 32);
+                        final int lon = (int) (value & 0xFFFFFFFF);
+                        if (predicate.test(lat, lon)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                };
+            }
+            case DISJOINT: {
+                final GeoEncodingUtils.Component2DPredicate predicate = GeoEncodingUtils.createComponentPredicate(
+                    LatLonGeometry.create(geometries)
+                );
+                return (values, count) -> {
+                    for (int i = 0; i < count; i++) {
+                        final long value = values[i];
+                        final int lat = (int) (value >>> 32);
+                        final int lon = (int) (value & 0xFFFFFFFF);
+                        if (predicate.test(lat, lon)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                };
+            }
+            case WITHIN: {
+                final GeoEncodingUtils.Component2DPredicate predicate = GeoEncodingUtils.createComponentPredicate(
+                    LatLonGeometry.create(geometries)
+                );
+                return (values, count) -> {
+                    for (int i = 0; i < count; i++) {
+                        final long value = values[i];
+                        final int lat = (int) (value >>> 32);
+                        final int lon = (int) (value & 0xFFFFFFFF);
+                        if (predicate.test(lat, lon) == false) {
+                            return false;
+                        }
+                    }
+                    return true;
+                };
+            }
+            case CONTAINS: {
+                final Component2D[] component2DS = new Component2D[geometries.length];
+                for (int i = 0; i < geometries.length; i++) {
+                    component2DS[i] = LatLonGeometry.create(geometries[i]);
+                }
+                return (values, count) -> {
+                    Component2D.WithinRelation answer = Component2D.WithinRelation.DISJOINT;
+                    for (int i = 0; i < count; i++) {
+                        final long value = values[i];
+                        final double lat = GeoEncodingUtils.decodeLatitude((int) (value >>> 32));
+                        final double lon = GeoEncodingUtils.decodeLongitude((int) (value & 0xFFFFFFFF));
+                        for (Component2D component2D : component2DS) {
+                            Component2D.WithinRelation withinRelation = component2D.withinPoint(lon, lat);
+                            if (withinRelation == Component2D.WithinRelation.NOTWITHIN) {
+                                return false;
+                            } else if (withinRelation != Component2D.WithinRelation.DISJOINT) {
+                                answer = withinRelation;
+                            }
+                        }
+                    }
+                    return answer == Component2D.WithinRelation.CANDIDATE;
+                };
+            }
+            default:
+                throw new IllegalArgumentException("Unknown spatial relationship [" + relation.getRelationName() + "]");
+        }
     }
 }

+ 28 - 10
x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/GeoPointScriptFieldGeoShapeQueryTests.java

@@ -7,6 +7,7 @@
 package org.elasticsearch.xpack.runtimefields.query;
 
 import org.apache.lucene.geo.Polygon;
+import org.elasticsearch.common.geo.ShapeRelation;
 
 import static org.hamcrest.Matchers.equalTo;
 
@@ -17,25 +18,42 @@ public class GeoPointScriptFieldGeoShapeQueryTests extends AbstractGeoPointScrip
 
     @Override
     protected GeoPointScriptFieldGeoShapeQuery createTestInstance() {
-        return new GeoPointScriptFieldGeoShapeQuery(randomScript(), leafFactory, randomAlphaOfLength(5), polygon1);
+        return new GeoPointScriptFieldGeoShapeQuery(
+            randomScript(),
+            leafFactory,
+            randomAlphaOfLength(5),
+            ShapeRelation.INTERSECTS,
+            polygon1
+        );
     }
 
     @Override
     protected GeoPointScriptFieldGeoShapeQuery copy(GeoPointScriptFieldGeoShapeQuery orig) {
-        return new GeoPointScriptFieldGeoShapeQuery(orig.script(), leafFactory, orig.fieldName(), polygon1);
+        return new GeoPointScriptFieldGeoShapeQuery(orig.script(), leafFactory, orig.fieldName(), ShapeRelation.INTERSECTS, polygon1);
     }
 
     @Override
     protected GeoPointScriptFieldGeoShapeQuery mutate(GeoPointScriptFieldGeoShapeQuery orig) {
-        if (randomBoolean()) {
-            new GeoPointScriptFieldGeoShapeQuery(
-                randomValueOtherThan(orig.script(), this::randomScript),
-                leafFactory,
-                orig.fieldName(),
-                polygon2
-            );
+        switch (randomInt(2)) {
+            case 0:
+                return new GeoPointScriptFieldGeoShapeQuery(
+                    randomValueOtherThan(orig.script(), this::randomScript),
+                    leafFactory,
+                    orig.fieldName(),
+                    ShapeRelation.INTERSECTS,
+                    polygon2
+                );
+            case 1:
+                return new GeoPointScriptFieldGeoShapeQuery(orig.script(), leafFactory, orig.fieldName(), ShapeRelation.DISJOINT, polygon1);
+            default:
+                return new GeoPointScriptFieldGeoShapeQuery(
+                    orig.script(),
+                    leafFactory,
+                    orig.fieldName() + "modified",
+                    ShapeRelation.INTERSECTS,
+                    polygon1
+                );
         }
-        return new GeoPointScriptFieldGeoShapeQuery(orig.script(), leafFactory, orig.fieldName() + "modified", polygon1);
     }
 
     @Override

+ 20 - 4
x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java

@@ -7,10 +7,14 @@
 package org.elasticsearch.xpack.spatial.index.mapper;
 
 import org.apache.lucene.document.LatLonShape;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.search.IndexOrDocValuesQuery;
+import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.Query;
 import org.elasticsearch.Version;
 import org.elasticsearch.common.Explicit;
+import org.elasticsearch.common.geo.GeoShapeUtils;
 import org.elasticsearch.common.geo.GeometryParser;
 import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
@@ -26,10 +30,10 @@ import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.mapper.Mapper;
 import org.elasticsearch.index.mapper.ParseContext;
+import org.elasticsearch.index.query.QueryShardException;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.search.lookup.SearchLookup;
 import org.elasticsearch.xpack.spatial.index.fielddata.plain.AbstractLatLonShapeIndexFieldData;
-import org.elasticsearch.xpack.spatial.index.query.VectorGeoShapeWithDocValuesQueryProcessor;
 import org.elasticsearch.xpack.spatial.search.aggregations.support.GeoShapeValuesSourceType;
 
 import java.util.Arrays;
@@ -128,8 +132,6 @@ public class GeoShapeWithDocValuesFieldMapper extends AbstractShapeGeometryField
 
     public static final class GeoShapeWithDocValuesFieldType extends AbstractShapeGeometryFieldType implements GeoShapeQueryable {
 
-        private final VectorGeoShapeWithDocValuesQueryProcessor queryProcessor = new VectorGeoShapeWithDocValuesQueryProcessor();
-
         public GeoShapeWithDocValuesFieldType(String name, boolean indexed, boolean hasDocValues,
                                               Orientation orientation, GeoShapeParser parser, Map<String, String> meta) {
             super(name, indexed, false, hasDocValues, false, parser, orientation, meta);
@@ -147,7 +149,21 @@ public class GeoShapeWithDocValuesFieldMapper extends AbstractShapeGeometryField
 
         @Override
         public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
-            return queryProcessor.geoShapeQuery(shape, fieldName, relation, context, hasDocValues());
+            // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0)
+            if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(Version.V_7_5_0)) {
+                throw new QueryShardException(context,
+                    ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "].");
+            }
+            final LatLonGeometry[] luceneGeometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, relation);
+            if (luceneGeometries.length == 0) {
+                return new MatchNoDocsQuery();
+            }
+            Query query = LatLonShape.newGeometryQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
+            if (hasDocValues()) {
+                final Query queryDocValues = new LatLonShapeDocValuesQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
+                query =  new IndexOrDocValuesQuery(query, queryDocValues);
+            }
+            return query;
         }
     }
 

+ 1 - 1
x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/LatLonShapeDocValuesQuery.java → x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/LatLonShapeDocValuesQuery.java

@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-package org.elasticsearch.xpack.spatial.index.query;
+package org.elasticsearch.xpack.spatial.index.mapper;
 
 import org.apache.lucene.document.ShapeField;
 import org.apache.lucene.geo.Component2D;

+ 0 - 77
x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/VectorGeoShapeWithDocValuesQueryProcessor.java

@@ -1,77 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/*
- * 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.xpack.spatial.index.query;
-
-import org.apache.lucene.document.LatLonShape;
-import org.apache.lucene.geo.LatLonGeometry;
-import org.apache.lucene.search.IndexOrDocValuesQuery;
-import org.apache.lucene.search.MatchNoDocsQuery;
-import org.apache.lucene.search.Query;
-import org.elasticsearch.Version;
-import org.elasticsearch.common.geo.GeoShapeUtils;
-import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.geometry.Geometry;
-import org.elasticsearch.geometry.Line;
-import org.elasticsearch.geometry.MultiLine;
-import org.elasticsearch.index.query.QueryShardException;
-import org.elasticsearch.index.query.SearchExecutionContext;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-public class VectorGeoShapeWithDocValuesQueryProcessor {
-
-    private static final List<Class<? extends Geometry>> WITHIN_UNSUPPORTED_GEOMETRIES = new ArrayList<>();
-    static {
-        WITHIN_UNSUPPORTED_GEOMETRIES.add(Line.class);
-        WITHIN_UNSUPPORTED_GEOMETRIES.add(MultiLine.class);
-    }
-
-    public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation,
-                               SearchExecutionContext context, boolean hasDocValues) {
-        // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0)
-        if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(Version.V_7_5_0)) {
-            throw new QueryShardException(context,
-                ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "].");
-        }
-        final LatLonGeometry[] luceneGeometries = relation == ShapeRelation.WITHIN ?
-                GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, WITHIN_UNSUPPORTED_GEOMETRIES) :
-                GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, Collections.emptyList());
-
-        if (luceneGeometries.length == 0) {
-            return new MatchNoDocsQuery();
-        }
-        Query query = LatLonShape.newGeometryQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
-        if (hasDocValues) {
-            final Query queryDocValues = new LatLonShapeDocValuesQuery(fieldName, relation.getLuceneRelation(), luceneGeometries);
-            query =  new IndexOrDocValuesQuery(query, queryDocValues);
-        }
-        return query;
-    }
-}
-

+ 0 - 84
x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/fielddata/GeometryDocValueTests.java

@@ -6,19 +6,8 @@
 
 package org.elasticsearch.xpack.spatial.index.fielddata;
 
-import org.apache.lucene.geo.LatLonGeometry;
-import org.elasticsearch.common.geo.GeoShapeUtils;
-import org.elasticsearch.geometry.Circle;
 import org.elasticsearch.geometry.Geometry;
 import org.elasticsearch.geometry.GeometryCollection;
-import org.elasticsearch.geometry.GeometryVisitor;
-import org.elasticsearch.geometry.Line;
-import org.elasticsearch.geometry.LinearRing;
-import org.elasticsearch.geometry.MultiLine;
-import org.elasticsearch.geometry.MultiPoint;
-import org.elasticsearch.geometry.MultiPolygon;
-import org.elasticsearch.geometry.Point;
-import org.elasticsearch.geometry.Polygon;
 import org.elasticsearch.geometry.Rectangle;
 import org.elasticsearch.geometry.ShapeType;
 import org.elasticsearch.index.mapper.GeoShapeIndexer;
@@ -26,7 +15,6 @@ import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.spatial.util.GeoTestUtils;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -117,76 +105,4 @@ public class GeometryDocValueTests extends ESTestCase {
         GeometryDocValueReader reader = GeoTestUtils.geometryDocValueReader(geometry, CoordinateEncoder.GEO);
         assertThat(reader.getDimensionalShapeType(), equalTo(expected));
     }
-
-    private static LatLonGeometry[] toLuceneGeometry(Geometry geometry) {
-        List<LatLonGeometry> luceneGeometries = new ArrayList<>();
-        geometry.visit(new GeometryVisitor<Void, RuntimeException>() {
-            @Override
-            public Void visit(Circle circle)  {
-                throw new UnsupportedOperationException();
-            }
-
-            @Override
-            public Void visit(GeometryCollection<?> collection)  {
-                for (Geometry g : collection) {
-                    g.visit(this);
-                }
-                return null;
-            }
-
-            @Override
-            public Void visit(Line line)  {
-                luceneGeometries.add(GeoShapeUtils.toLuceneLine(line));
-                return null;
-            }
-
-            @Override
-            public Void visit(LinearRing ring)  {
-                throw new UnsupportedOperationException();
-            }
-
-            @Override
-            public Void visit(MultiLine multiLine) {
-                for(Line line : multiLine) {
-                    visit(line);
-                }
-                return null;
-            }
-
-            @Override
-            public Void visit(MultiPoint multiPoint) {
-                for(Point point : multiPoint) {
-                    visit(point);
-                }
-                return null;
-            }
-
-            @Override
-            public Void visit(MultiPolygon multiPolygon)  {
-                for(Polygon polygon : multiPolygon) {
-                    visit(polygon);
-                }
-                return null;
-            }
-
-            @Override
-            public Void visit(Point point)  {
-                luceneGeometries.add(GeoShapeUtils.toLucenePoint(point));
-                return null;
-            }
-
-            @Override
-            public Void visit(Polygon polygon) {
-                luceneGeometries.add(GeoShapeUtils.toLucenePolygon(polygon));
-                return null;
-            }
-
-            @Override
-            public Void visit(Rectangle rectangle) {
-                luceneGeometries.add(GeoShapeUtils.toLuceneRectangle(rectangle));
-                return null;
-            }
-        });
-        return luceneGeometries.toArray(new LatLonGeometry[luceneGeometries.size()]);
-    }
 }

+ 1 - 2
x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LatLonShapeDocValuesQueryTests.java → x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/LatLonShapeDocValuesQueryTests.java

@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-package org.elasticsearch.xpack.spatial.index.query;
+package org.elasticsearch.xpack.spatial.index.mapper;
 
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.LatLonShape;
@@ -30,7 +30,6 @@ import org.elasticsearch.geo.GeometryTestUtils;
 import org.elasticsearch.geometry.Geometry;
 import org.elasticsearch.index.mapper.GeoShapeIndexer;
 import org.elasticsearch.test.ESTestCase;
-import org.elasticsearch.xpack.spatial.index.mapper.BinaryGeoShapeDocValuesField;
 
 import java.io.IOException;
 import java.util.List;

+ 3 - 5
x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/ingest/CircleProcessorTests.java

@@ -31,7 +31,6 @@ import org.elasticsearch.geometry.utils.WellKnownText;
 import org.elasticsearch.index.mapper.GeoShapeIndexer;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.query.SearchExecutionContext;
-import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
 import org.elasticsearch.ingest.IngestDocument;
 import org.elasticsearch.ingest.RandomDocumentPicks;
 import org.elasticsearch.test.ESTestCase;
@@ -214,14 +213,13 @@ public class CircleProcessorTests extends ESTestCase {
         int numSides = randomIntBetween(4, 1000);
         Geometry geometry = SpatialUtils.createRegularGeoShapePolygon(circle, numSides);
 
-        MappedFieldType shapeType
+        GeoShapeWithDocValuesFieldType shapeType
             = new GeoShapeWithDocValuesFieldType(fieldName, true, false, ShapeBuilder.Orientation.RIGHT, null, Collections.emptyMap());
 
-        VectorGeoShapeQueryProcessor processor = new VectorGeoShapeQueryProcessor();
         SearchExecutionContext mockedContext = mock(SearchExecutionContext.class);
         when(mockedContext.getFieldType(any())).thenReturn(shapeType);
-        Query sameShapeQuery = processor.geoShapeQuery(geometry, fieldName, ShapeRelation.INTERSECTS, mockedContext);
-        Query pointOnDatelineQuery = processor.geoShapeQuery(new Point(180, circle.getLat()), fieldName,
+        Query sameShapeQuery = shapeType.geoShapeQuery(geometry, fieldName, ShapeRelation.INTERSECTS, mockedContext);
+        Query pointOnDatelineQuery = shapeType.geoShapeQuery(new Point(180, circle.getLat()), fieldName,
             ShapeRelation.INTERSECTS, mockedContext);
 
         try (Directory dir = newDirectory(); RandomIndexWriter w = new RandomIndexWriter(random(), dir)) {

+ 46 - 1
x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/101_geo_point_from_source.yml

@@ -83,7 +83,7 @@ setup:
   - match: {hits.total.value: 1}
 
 ---
-"geo shape query":
+"geo shape intersects query":
   - do:
       search:
         index: locations
@@ -96,6 +96,51 @@ setup:
                   coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
   - match: {hits.total.value: 1}
 
+---
+"geo shape within query":
+  - do:
+      search:
+        index: locations
+        body:
+          query:
+            geo_shape:
+              location:
+                shape:
+                  type: "envelope"
+                  coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
+                relation: within
+  - match: {hits.total.value: 1}
+
+---
+"geo shape disjoint query":
+  - do:
+      search:
+        index: locations
+        body:
+          query:
+            geo_shape:
+              location:
+                shape:
+                  type: "envelope"
+                  coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
+                relation: disjoint
+  - match: {hits.total.value: 5}
+
+---
+"geo shape contains query":
+  - do:
+      search:
+        index: locations
+        body:
+          query:
+            geo_shape:
+              location:
+                shape:
+                  type: "envelope"
+                  coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
+                relation: contains
+  - match: {hits.total.value: 0}
+
 ---
 "geo distance query":
   - do:

+ 55 - 1
x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/102_geo_point_source_in_query.yml

@@ -81,7 +81,7 @@ setup:
   - match: {hits.total.value: 1}
 
 ---
-"geo shape query":
+"geo shape intersects query":
   - do:
       search:
         index: locations
@@ -97,6 +97,60 @@ setup:
                   coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
   - match: {hits.total.value: 1}
 
+---
+"geo shape within query":
+  - do:
+      search:
+        index: locations
+        body:
+          runtime_mappings:
+            location:
+              type: geo_point
+          query:
+            geo_shape:
+              location:
+                shape:
+                  type: "envelope"
+                  coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
+                relation: within
+  - match: {hits.total.value: 1}
+
+---
+"geo shape disjoint query":
+  - do:
+      search:
+        index: locations
+        body:
+          runtime_mappings:
+            location:
+              type: geo_point
+          query:
+            geo_shape:
+              location:
+                shape:
+                  type: "envelope"
+                  coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
+                relation: disjoint
+  - match: {hits.total.value: 5}
+
+---
+"geo shape contains query":
+  - do:
+      search:
+        index: locations
+        body:
+          runtime_mappings:
+            location:
+              type: geo_point
+          query:
+            geo_shape:
+              location:
+                shape:
+                  type: "envelope"
+                  coordinates: [ [ -10, 10 ], [ 10, -10 ] ]
+                relation: contains
+  - match: {hits.total.value: 0}
+
 ---
 "geo distance query":
   - do: