소스 검색

Geo shape query vs geo point (#52382)

Enable geo_shape query to work on geo_point fields for shapes: circle, polygon, multipolygon, rectangle

see: #48928

Co-Authored-By:  @iverase
Dominic Page 5 년 전
부모
커밋
d1cbdfb753
21개의 변경된 파일1002개의 추가작업 그리고 157개의 파일을 삭제
  1. 5 3
      docs/reference/query-dsl/geo-queries.asciidoc
  2. 17 3
      docs/reference/query-dsl/geo-shape-query.asciidoc
  3. 1 1
      libs/geo/src/main/java/org/elasticsearch/geometry/LinearRing.java
  4. 2 26
      server/src/main/java/org/elasticsearch/index/mapper/AbstractGeometryFieldMapper.java
  5. 63 0
      server/src/main/java/org/elasticsearch/index/mapper/AbstractSearchableGeometryFieldType.java
  6. 12 2
      server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java
  7. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/GeoShapeIndexer.java
  8. 4 4
      server/src/main/java/org/elasticsearch/index/query/AbstractGeometryQueryBuilder.java
  9. 17 11
      server/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java
  10. 2 1
      server/src/main/java/org/elasticsearch/index/query/LegacyGeoShapeQueryProcessor.java
  11. 191 0
      server/src/main/java/org/elasticsearch/index/query/VectorGeoPointShapeQueryProcessor.java
  12. 6 5
      server/src/main/java/org/elasticsearch/index/query/VectorGeoShapeQueryProcessor.java
  13. 1 2
      server/src/test/java/org/elasticsearch/common/geo/GeometryIndexerTests.java
  14. 65 0
      server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderGeoPointTests.java
  15. 84 0
      server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderGeoShapeTests.java
  16. 7 56
      server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java
  17. 138 3
      server/src/test/java/org/elasticsearch/search/geo/GeoPointShapeQueryTests.java
  18. 373 24
      server/src/test/java/org/elasticsearch/search/geo/GeoQueryTests.java
  19. 0 4
      server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java
  20. 10 9
      x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryBuilder.java
  21. 2 2
      x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryProcessor.java

+ 5 - 3
docs/reference/query-dsl/geo-queries.asciidoc

@@ -18,9 +18,11 @@ Finds documents with geo-points within the specified distance of a central point
 Find documents with geo-points within the specified polygon.
 
 <<query-dsl-geo-shape-query,`geo_shape`>> query::
-Finds documents with geo-shapes which either intersect, are contained by, or do not intersect with the specified
-geo-shape.
-
+Finds documents with:
+* `geo-shapes` which either intersect, are contained by, or do not intersect
+with the specified geo-shape
+* `geo-points` which intersect the specified
+geo-shape
 
 include::geo-bounding-box-query.asciidoc[]
 

+ 17 - 3
docs/reference/query-dsl/geo-shape-query.asciidoc

@@ -4,9 +4,9 @@
 <titleabbrev>Geo-shape</titleabbrev>
 ++++
 
-Filter documents indexed using the `geo_shape` type.
+Filter documents indexed using the `geo_shape` or `geo_point` type.
 
-Requires the <<geo-shape,`geo_shape` Mapping>>.
+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
 `geo_shape` mapping to find documents that have a shape that intersects
@@ -142,7 +142,7 @@ GET /example/_search
 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:
+The following is a complete list of spatial relation operators available when searching a field of type `geo_shape`:
 
 * `INTERSECTS` - (default) Return all documents whose `geo_shape` field
 intersects the query geometry.
@@ -153,6 +153,11 @@ is within 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.
+
 [float]
 ==== Ignore Unmapped
 
@@ -162,6 +167,15 @@ 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`
+
 ==== 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 to false.

+ 1 - 1
libs/geo/src/main/java/org/elasticsearch/geometry/LinearRing.java

@@ -64,6 +64,6 @@ public class LinearRing extends Line {
     public String toString() {
         return "linearring(x=" + Arrays.toString(getX()) +
             ", y=" + Arrays.toString(getY()) +
-            (hasZ() ? ", z=" + Arrays.toString(getZ()) : "");
+            (hasZ() ? ", z=" + Arrays.toString(getZ()) : "") + ")";
     }
 }

+ 2 - 26
server/src/main/java/org/elasticsearch/index/mapper/AbstractGeometryFieldMapper.java

@@ -26,8 +26,6 @@ import org.apache.lucene.search.TermQuery;
 import org.elasticsearch.Version;
 import org.elasticsearch.common.Explicit;
 import org.elasticsearch.common.ParseField;
-import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.common.geo.SpatialStrategy;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
 import org.elasticsearch.common.settings.Settings;
@@ -35,7 +33,6 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.support.XContentMapValues;
-import org.elasticsearch.geometry.Geometry;
 import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.DeprecatedParameters;
 import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.query.QueryShardException;
@@ -91,19 +88,6 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
 
     }
 
-    /**
-     * interface representing a query builder that generates a query from the given shape
-     */
-    public interface QueryProcessor {
-        Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context);
-
-        @Deprecated
-        default Query process(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation,
-                              QueryShardContext context) {
-            return process(shape, fieldName, relation, context);
-        }
-    }
-
     public abstract static class Builder<T extends Builder, Y extends AbstractGeometryFieldMapper>
             extends FieldMapper.Builder<T, Y> {
         protected Boolean coerce;
@@ -272,14 +256,14 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
         }
     }
 
-    public abstract static class AbstractGeometryFieldType<Parsed, Processed> extends MappedFieldType {
+    public abstract static class AbstractGeometryFieldType<Parsed, Processed> extends AbstractSearchableGeometryFieldType {
         protected Orientation orientation = Defaults.ORIENTATION.value();
 
         protected Indexer<Parsed, Processed> geometryIndexer;
 
         protected Parser<Parsed> geometryParser;
 
-        protected QueryProcessor geometryQueryBuilder;
+
 
         protected AbstractGeometryFieldType() {
             setIndexOptions(IndexOptions.DOCS);
@@ -339,14 +323,6 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
         protected Parser<Parsed> geometryParser() {
             return geometryParser;
         }
-
-        public void setGeometryQueryBuilder(QueryProcessor geometryQueryBuilder)  {
-            this.geometryQueryBuilder = geometryQueryBuilder;
-        }
-
-        public QueryProcessor geometryQueryBuilder() {
-            return geometryQueryBuilder;
-        }
     }
 
     protected Explicit<Boolean> coerce;

+ 63 - 0
server/src/main/java/org/elasticsearch/index/mapper/AbstractSearchableGeometryFieldType.java

@@ -0,0 +1,63 @@
+/*
+ * 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.mapper;
+
+import org.apache.lucene.search.Query;
+import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.SpatialStrategy;
+import org.elasticsearch.geometry.Geometry;
+import org.elasticsearch.index.query.QueryShardContext;
+
+/**
+ * a base class for geometry types that support shape query builder
+ */
+public abstract class AbstractSearchableGeometryFieldType extends MappedFieldType {
+
+    protected QueryProcessor geometryQueryBuilder;
+
+    protected AbstractSearchableGeometryFieldType() {
+    }
+
+    protected AbstractSearchableGeometryFieldType(AbstractSearchableGeometryFieldType ref) {
+        super(ref);
+    }
+
+    public void setGeometryQueryBuilder(QueryProcessor geometryQueryBuilder)  {
+        this.geometryQueryBuilder = geometryQueryBuilder;
+    }
+
+    public QueryProcessor geometryQueryBuilder() {
+        return geometryQueryBuilder;
+    }
+
+    /**
+     * interface representing a query builder that generates a query from the given shape
+     */
+    public interface QueryProcessor {
+        Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context);
+
+        @Deprecated
+        default Query process(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation,
+                              QueryShardContext context) {
+            return process(shape, fieldName, relation, context);
+        }
+    }
+}
+

+ 12 - 2
server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java

@@ -40,6 +40,7 @@ import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointDVIndexFieldData;
 import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.query.QueryShardException;
+import org.elasticsearch.index.query.VectorGeoPointShapeQueryProcessor;
 import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
 import org.elasticsearch.search.aggregations.support.ValuesSourceType;
 
@@ -123,6 +124,14 @@ public class GeoPointFieldMapper extends FieldMapper implements ArrayValueMapper
                 ignoreMalformed, ignoreZValue, copyTo);
         }
 
+        @Override
+        protected void setupFieldType(BuilderContext context) {
+            super.setupFieldType(context);
+
+            GeoPointFieldType fieldType = (GeoPointFieldType)fieldType();
+            fieldType.setGeometryQueryBuilder(new VectorGeoPointShapeQueryProcessor());
+        }
+
         @Override
         public GeoPointFieldMapper build(BuilderContext context) {
             return build(context, name, fieldType, defaultFieldType, context.indexSettings(),
@@ -213,7 +222,7 @@ public class GeoPointFieldMapper extends FieldMapper implements ArrayValueMapper
         throw new UnsupportedOperationException("Parsing is implemented in parse(), this method should NEVER be called");
     }
 
-    public static class GeoPointFieldType extends MappedFieldType {
+    public static class GeoPointFieldType extends AbstractSearchableGeometryFieldType {
         public GeoPointFieldType() {
         }
 
@@ -253,7 +262,8 @@ public class GeoPointFieldMapper extends FieldMapper implements ArrayValueMapper
 
         @Override
         public Query termQuery(Object value, QueryShardContext context) {
-            throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead: ["
+            throw new QueryShardException(context,
+                "Geo fields do not support exact searching, use dedicated geo queries instead: ["
                 + name() + "]");
         }
     }

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/GeoShapeIndexer.java

@@ -24,6 +24,7 @@ import org.apache.lucene.document.LatLonShape;
 import org.apache.lucene.index.IndexableField;
 import org.elasticsearch.common.geo.GeoLineDecomposer;
 import org.elasticsearch.common.geo.GeoPolygonDecomposer;
+import org.elasticsearch.common.geo.GeoShapeType;
 import org.elasticsearch.geometry.Circle;
 import org.elasticsearch.geometry.Geometry;
 import org.elasticsearch.geometry.GeometryCollection;
@@ -64,7 +65,7 @@ public final class GeoShapeIndexer implements AbstractGeometryFieldMapper.Indexe
         return geometry.visit(new GeometryVisitor<>() {
             @Override
             public Geometry visit(Circle circle) {
-                throw new UnsupportedOperationException("CIRCLE geometry is not supported");
+                throw new UnsupportedOperationException(GeoShapeType.CIRCLE + " geometry is not supported");
             }
 
             @Override

+ 4 - 4
server/src/main/java/org/elasticsearch/index/query/AbstractGeometryQueryBuilder.java

@@ -337,11 +337,9 @@ public abstract class AbstractGeometryQueryBuilder<QB extends AbstractGeometryQu
     }
 
     /** list of content types this shape query is compatible with */
-    protected abstract List validContentTypes();
+    protected abstract List<String> validContentTypes();
     /** builds the appropriate lucene shape query */
     protected abstract Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType);
-    /** returns expected content type for this query */
-    protected abstract String queryFieldType();
     /** writes the xcontent specific to this shape query */
     protected abstract void doShapeQueryXContent(XContentBuilder builder, Params params) throws IOException;
     /** creates a new ShapeQueryBuilder from the provided field name and shape builder */
@@ -365,7 +363,9 @@ public abstract class AbstractGeometryQueryBuilder<QB extends AbstractGeometryQu
             if (ignoreUnmapped) {
                 return new MatchNoDocsQuery();
             } else {
-                throw new QueryShardException(context, "failed to find " + queryFieldType() + " field [" + fieldName + "]");
+                throw new QueryShardException(context, "failed to find "
+                    + String.join(" or ", validContentTypes())
+                    + " field [" + fieldName + "]");
             }
         }
 

+ 17 - 11
server/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java

@@ -34,12 +34,14 @@ import org.elasticsearch.common.logging.DeprecationLogger;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.geometry.Geometry;
-import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
+import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
+import org.elasticsearch.index.mapper.GeoPointFieldMapper;
 import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Supplier;
@@ -56,6 +58,12 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
 
     private SpatialStrategy strategy;
 
+    protected static final List<String> validContentTypes =
+        Collections.unmodifiableList(
+            Arrays.asList(
+                GeoShapeFieldMapper.CONTENT_TYPE,
+                GeoPointFieldMapper.CONTENT_TYPE));
+
     /**
      * Creates a new GeoShapeQueryBuilder whose Query will be against the given
      * field name using the given Shape
@@ -168,13 +176,8 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
     }
 
     @Override
-    protected List validContentTypes() {
-        return Arrays.asList(GeoShapeFieldMapper.CONTENT_TYPE);
-    }
-
-    @Override
-    public String queryFieldType() {
-        return GeoShapeFieldMapper.CONTENT_TYPE;
+    protected List<String> validContentTypes() {
+        return validContentTypes;
     }
 
     @Override
@@ -197,12 +200,15 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
 
     @Override
     public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) {
-        if (fieldType.typeName().equals(GeoShapeFieldMapper.CONTENT_TYPE) == false) {
+        if (validContentTypes().contains(fieldType.typeName()) == false) {
             throw new QueryShardException(context,
-                "Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]");
+                "Field [" + fieldName + "] is of unsupported type [" + fieldType.typeName() + "]. ["
+                + NAME + "] query supports the following types ["
+                + String.join(",", validContentTypes()) +  "]");
         }
 
-        final AbstractGeometryFieldMapper.AbstractGeometryFieldType ft = (AbstractGeometryFieldMapper.AbstractGeometryFieldType) fieldType;
+        final AbstractSearchableGeometryFieldType ft =
+            (AbstractSearchableGeometryFieldType) fieldType;
         return new ConstantScoreQuery(ft.geometryQueryBuilder().process(shape, fieldName, strategy, relation, context));
     }
 

+ 2 - 1
server/src/main/java/org/elasticsearch/index/query/LegacyGeoShapeQueryProcessor.java

@@ -53,6 +53,7 @@ import org.elasticsearch.geometry.Point;
 import org.elasticsearch.geometry.Polygon;
 import org.elasticsearch.geometry.Rectangle;
 import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
+import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
 import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.spatial4j.shape.Shape;
@@ -62,7 +63,7 @@ import java.util.List;
 
 import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;
 
-public class LegacyGeoShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
+public class LegacyGeoShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
 
     private AbstractGeometryFieldMapper.AbstractGeometryFieldType ft;
 

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

@@ -0,0 +1,191 @@
+/*
+ * 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.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.AbstractSearchableGeometryFieldType;
+import org.elasticsearch.index.mapper.GeoPointFieldMapper;
+import org.elasticsearch.index.mapper.MappedFieldType;
+
+import java.util.ArrayList;
+
+import static org.elasticsearch.index.mapper.GeoShapeIndexer.toLucenePolygon;
+
+public class VectorGeoPointShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
+
+    @Override
+    public Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext 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, QueryShardContext context) {
+        MappedFieldType fieldType = context.fieldMapper(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, QueryShardContext context) {
+        ShapeVisitor shapeVisitor = new ShapeVisitor(context, fieldName, relation);
+        return queryShape.visit(shapeVisitor);
+    }
+
+    private class ShapeVisitor implements GeometryVisitor<Query, RuntimeException> {
+        QueryShardContext context;
+        MappedFieldType fieldType;
+        String fieldName;
+        ShapeRelation relation;
+
+        ShapeVisitor(QueryShardContext context, String fieldName, ShapeRelation relation) {
+            this.context = context;
+            this.fieldType = context.fieldMapper(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] = 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;
+        }
+    }
+}
+

+ 6 - 5
server/src/main/java/org/elasticsearch/index/query/VectorGeoShapeQueryProcessor.java

@@ -39,14 +39,14 @@ import org.elasticsearch.geometry.MultiPoint;
 import org.elasticsearch.geometry.MultiPolygon;
 import org.elasticsearch.geometry.Point;
 import org.elasticsearch.geometry.Rectangle;
-import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
+import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
 import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.GeoShapeIndexer;
 import org.elasticsearch.index.mapper.MappedFieldType;
 
 import static org.elasticsearch.index.mapper.GeoShapeIndexer.toLucenePolygon;
 
-public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
+public class VectorGeoShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
 
     @Override
     public Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context) {
@@ -59,7 +59,8 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
         return getVectorQueryFromShape(shape, fieldName, relation, context);
     }
 
-    protected Query getVectorQueryFromShape(Geometry queryShape, String fieldName, ShapeRelation relation, QueryShardContext context) {
+    protected Query getVectorQueryFromShape(
+        Geometry queryShape, String fieldName, ShapeRelation relation, QueryShardContext context) {
         GeoShapeIndexer geometryIndexer = new GeoShapeIndexer(true, fieldName);
 
         Geometry processedShape = geometryIndexer.prepareForIndexing(queryShape);
@@ -85,7 +86,7 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
 
         @Override
         public Query visit(Circle circle) {
-            throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape Circle");
+            throw new QueryShardException(context, "Field [" + fieldName + "] found an unknown shape Circle");
         }
 
         @Override
@@ -117,7 +118,7 @@ public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper
 
         @Override
         public Query visit(LinearRing ring) {
-            throw new QueryShardException(context, "Field [" + fieldName + "] found and unsupported shape LinearRing");
+            throw new QueryShardException(context, "Field [" + fieldName + "] found an unsupported shape LinearRing");
         }
 
         @Override

+ 1 - 2
server/src/test/java/org/elasticsearch/common/geo/GeometryIndexerTests.java

@@ -35,7 +35,6 @@ import org.elasticsearch.geometry.Point;
 import org.elasticsearch.geometry.Polygon;
 import org.elasticsearch.index.mapper.GeoShapeIndexer;
 import org.elasticsearch.test.ESTestCase;
-
 import java.io.IOException;
 import java.text.ParseException;
 import java.util.ArrayList;
@@ -52,7 +51,7 @@ public class GeometryIndexerTests extends ESTestCase {
     public void testCircle() {
         UnsupportedOperationException ex =
             expectThrows(UnsupportedOperationException.class, () -> indexer.prepareForIndexing(new Circle(2, 1, 3)));
-        assertEquals("CIRCLE geometry is not supported", ex.getMessage());
+        assertEquals(GeoShapeType.CIRCLE + " geometry is not supported", ex.getMessage());
     }
 
     public void testCollection() {

+ 65 - 0
server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderGeoPointTests.java

@@ -0,0 +1,65 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.index.query;
+
+import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.builders.ShapeBuilder;
+import org.elasticsearch.test.geo.RandomShapeGenerator;
+
+public class GeoShapeQueryBuilderGeoPointTests extends GeoShapeQueryBuilderTests {
+
+    protected String fieldName() {
+        return GEO_POINT_FIELD_NAME;
+    }
+
+    protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
+        RandomShapeGenerator.ShapeType shapeType = RandomShapeGenerator.ShapeType.POLYGON;
+        ShapeBuilder<?, ?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
+        GeoShapeQueryBuilder builder;
+        clearShapeFields();
+        if (indexedShape == false) {
+            builder = new GeoShapeQueryBuilder(fieldName(), shape);
+        } else {
+            indexedShapeToReturn = shape;
+            indexedShapeId = randomAlphaOfLengthBetween(3, 20);
+            builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId);
+            if (randomBoolean()) {
+                indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
+                builder.indexedShapeIndex(indexedShapeIndex);
+            }
+            if (randomBoolean()) {
+                indexedShapePath = randomAlphaOfLengthBetween(3, 20);
+                builder.indexedShapePath(indexedShapePath);
+            }
+            if (randomBoolean()) {
+                indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
+                builder.indexedShapeRouting(indexedShapeRouting);
+            }
+        }
+        if (randomBoolean()) {
+            builder.relation(randomFrom(ShapeRelation.INTERSECTS));
+        }
+
+        if (randomBoolean()) {
+            builder.ignoreUnmapped(randomBoolean());
+        }
+        return builder;
+    }
+
+}

+ 84 - 0
server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderGeoShapeTests.java

@@ -0,0 +1,84 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.index.query;
+
+import org.elasticsearch.Version;
+import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.builders.ShapeBuilder;
+import org.elasticsearch.test.geo.RandomShapeGenerator;
+
+public class GeoShapeQueryBuilderGeoShapeTests extends GeoShapeQueryBuilderTests {
+
+    protected String fieldName() {
+        return GEO_SHAPE_FIELD_NAME;
+    }
+
+    protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
+        RandomShapeGenerator.ShapeType shapeType = randomFrom(
+            RandomShapeGenerator.ShapeType.POINT,
+            RandomShapeGenerator.ShapeType.MULTIPOINT,
+            RandomShapeGenerator.ShapeType.LINESTRING,
+            RandomShapeGenerator.ShapeType.MULTILINESTRING,
+            RandomShapeGenerator.ShapeType.POLYGON);
+        ShapeBuilder<?, ?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
+        GeoShapeQueryBuilder builder;
+        clearShapeFields();
+        if (indexedShape == false) {
+            builder = new GeoShapeQueryBuilder(fieldName(), shape);
+        } else {
+            indexedShapeToReturn = shape;
+            indexedShapeId = randomAlphaOfLengthBetween(3, 20);
+            builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId);
+            if (randomBoolean()) {
+                indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
+                builder.indexedShapeIndex(indexedShapeIndex);
+            }
+            if (randomBoolean()) {
+                indexedShapePath = randomAlphaOfLengthBetween(3, 20);
+                builder.indexedShapePath(indexedShapePath);
+            }
+            if (randomBoolean()) {
+                indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
+                builder.indexedShapeRouting(indexedShapeRouting);
+            }
+        }
+        if (randomBoolean()) {
+            QueryShardContext context = createShardContext();
+            if (context.indexVersionCreated().onOrAfter(Version.V_7_5_0)) { // CONTAINS is only supported from version 7.5
+                if (shapeType == RandomShapeGenerator.ShapeType.LINESTRING || shapeType == RandomShapeGenerator.ShapeType.MULTILINESTRING) {
+                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.CONTAINS));
+                } else {
+                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS,
+                        ShapeRelation.WITHIN, ShapeRelation.CONTAINS));
+                }
+            } else {
+                if (shapeType == RandomShapeGenerator.ShapeType.LINESTRING || shapeType == RandomShapeGenerator.ShapeType.MULTILINESTRING) {
+                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
+                } else {
+                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
+                }
+            }
+        }
+
+        if (randomBoolean()) {
+            builder.ignoreUnmapped(randomBoolean());
+        }
+        return builder;
+    }
+}

+ 7 - 56
server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java

@@ -29,7 +29,6 @@ import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.cluster.metadata.IndexMetaData;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
-import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.io.stream.BytesStreamOutput;
@@ -48,13 +47,13 @@ import org.locationtech.jts.geom.Coordinate;
 
 import java.io.IOException;
 
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.matchesPattern;
 
-public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQueryBuilder> {
+public abstract class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQueryBuilder> {
 
     protected static String indexedShapeId;
     protected static String indexedShapePath;
@@ -62,9 +61,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
     protected static String indexedShapeRouting;
     protected static ShapeBuilder<?, ?, ?> indexedShapeToReturn;
 
-    protected String fieldName() {
-        return GEO_SHAPE_FIELD_NAME;
-    }
+    protected abstract String fieldName();
 
     @Override
     protected Settings createTestIndexSettings() {
@@ -81,54 +78,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
         return doCreateTestQueryBuilder(randomBoolean());
     }
 
-    protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
-        RandomShapeGenerator.ShapeType shapeType =
-            randomFrom(ShapeType.POINT, ShapeType.MULTIPOINT, ShapeType.LINESTRING, ShapeType.MULTILINESTRING, ShapeType.POLYGON);
-        ShapeBuilder<?, ?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
-        GeoShapeQueryBuilder builder;
-        clearShapeFields();
-        if (indexedShape == false) {
-            builder = new GeoShapeQueryBuilder(fieldName(), shape);
-        } else {
-            indexedShapeToReturn = shape;
-            indexedShapeId = randomAlphaOfLengthBetween(3, 20);
-            builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId);
-            if (randomBoolean()) {
-                indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
-                builder.indexedShapeIndex(indexedShapeIndex);
-            }
-            if (randomBoolean()) {
-                indexedShapePath = randomAlphaOfLengthBetween(3, 20);
-                builder.indexedShapePath(indexedShapePath);
-            }
-            if (randomBoolean()) {
-                indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
-                builder.indexedShapeRouting(indexedShapeRouting);
-            }
-        }
-        if (randomBoolean()) {
-            QueryShardContext context = createShardContext();
-            if (context.indexVersionCreated().onOrAfter(Version.V_7_5_0)) { // CONTAINS is only supported from version 7.5
-                if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
-                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.CONTAINS));
-                } else {
-                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS,
-                        ShapeRelation.WITHIN, ShapeRelation.CONTAINS));
-                }
-            } else {
-                if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
-                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
-                } else {
-                    builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
-                }
-            }
-        }
-
-        if (randomBoolean()) {
-            builder.ignoreUnmapped(randomBoolean());
-        }
-        return builder;
-    }
+    abstract GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape);
 
     @Override
     protected GetResponse executeGet(GetRequest getRequest) {
@@ -276,7 +226,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
             new GeoShapeQueryBuilder("unmapped", shape.buildGeometry());
         failingQueryBuilder.ignoreUnmapped(false);
         QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(createShardContext()));
-        assertThat(e.getMessage(), containsString("failed to find geo_shape field [unmapped]"));
+        assertThat(e.getMessage(), matchesPattern("failed to find .*geo_shape.* field \\[unmapped\\]"));
     }
 
     public void testWrongFieldType() throws IOException {
@@ -286,7 +236,8 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
             new GeoShapeQueryBuilder(STRING_FIELD_NAME, shape) :
             new GeoShapeQueryBuilder(STRING_FIELD_NAME, shape.buildGeometry());
         QueryShardException e = expectThrows(QueryShardException.class, () -> queryBuilder.toQuery(createShardContext()));
-        assertThat(e.getMessage(), containsString("Field [mapped_string] is not of type [geo_shape] but of type [text]"));
+        assertThat(e.getMessage(), matchesPattern("Field \\[mapped_string\\] is of unsupported type \\[text\\]." +
+            " \\[geo_shape\\] query supports the following types \\[.*geo_shape.*\\]"));
     }
 
     public void testSerializationFailsUnlessFetched() throws IOException {

+ 138 - 3
server/src/test/java/org/elasticsearch/search/geo/GeoPointShapeQueryTests.java

@@ -19,23 +19,158 @@
 
 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 {
 
     @Override
     protected XContentBuilder createDefaultMapping() throws Exception {
         XContentBuilder xcb = XContentFactory.jsonBuilder().startObject()
-            .startObject("properties").startObject("location")
+            .startObject("properties").startObject(defaultGeoFieldName)
             .field("type", "geo_point")
             .endObject().endObject().endObject();
 
         return xcb;
     }
 
-    public void testIndexPointsFilterRectangle() throws Exception {
-        //super.testIndexPointsFilterRectangle(Strings.toString(createDefaultMapping()));
+    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"));
+        }
     }
 
 }

+ 373 - 24
server/src/test/java/org/elasticsearch/search/geo/GeoQueryTests.java

@@ -22,59 +22,76 @@ package org.elasticsearch.search.geo;
 import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.geo.GeoShapeType;
+import org.elasticsearch.common.geo.ShapeRelation;
+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.MultiPolygonBuilder;
+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.Rectangle;
+import org.elasticsearch.index.query.GeoShapeQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHits;
 import org.elasticsearch.test.ESSingleNodeTestCase;
 import org.locationtech.jts.geom.Coordinate;
 
 import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
-import static org.elasticsearch.index.query.QueryBuilders.geoIntersectionQuery;
-import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.nullValue;
 
 public abstract class GeoQueryTests extends ESSingleNodeTestCase {
 
     protected abstract XContentBuilder createDefaultMapping() throws Exception;
 
+    static String defaultGeoFieldName = "geo";
+    static String defaultIndexName = "test";
+
     public void testNullShape() throws Exception {
         String mapping = Strings.toString(createDefaultMapping());
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        client().admin().indices().prepareCreate(defaultIndexName).setMapping(mapping).get();
         ensureGreen();
 
-        client().prepareIndex("test").setId("aNullshape").setSource("{\"geo\": null}", XContentType.JSON)
+        client().prepareIndex(defaultIndexName)
+            .setId("aNullshape")
+            .setSource("{\"geo\": null}", XContentType.JSON)
             .setRefreshPolicy(IMMEDIATE).get();
-        GetResponse result = client().prepareGet("test", "aNullshape").get();
+        GetResponse result = client().prepareGet(defaultIndexName, "aNullshape").get();
         assertThat(result.getField("location"), nullValue());
     };
 
-    public void testIndexPointsFilterRectangle(String mapping) throws Exception {
-        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+    public void testIndexPointsFilterRectangle() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate(defaultIndexName).setMapping(mapping).get();
         ensureGreen();
 
-        client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject()
-            .field("name", "Document 1")
-            .startObject("geo")
-            .field("type", "point")
-            .startArray("coordinates").value(-30).value(-30).endArray()
-            .endObject()
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+              .field("name", "Document 1")
+              .field(defaultGeoFieldName, "POINT(-30 -30)")
             .endObject()).setRefreshPolicy(IMMEDIATE).get();
 
-        client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject()
-            .field("name", "Document 2")
-            .startObject("geo")
-            .field("type", "point")
-            .startArray("coordinates").value(-45).value(-50).endArray()
-            .endObject()
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+              .field("name", "Document 2")
+              .field(defaultGeoFieldName, "POINT(-45 -50)")
             .endObject()).setRefreshPolicy(IMMEDIATE).get();
 
         EnvelopeBuilder shape = new EnvelopeBuilder(new Coordinate(-45, 45), new Coordinate(45, -45));
-
-        SearchResponse searchResponse = client().prepareSearch("test")
-            .setQuery(geoIntersectionQuery("geo", shape))
+        GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(shape);
+        Geometry geometry = builder.buildGeometry().get(0);
+        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+            .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
+                .relation(ShapeRelation.INTERSECTS))
             .get();
 
         assertSearchResponse(searchResponse);
@@ -82,8 +99,9 @@ public abstract class GeoQueryTests extends ESSingleNodeTestCase {
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
 
-        searchResponse = client().prepareSearch("test")
-            .setQuery(geoShapeQuery("geo", shape))
+        // default query, without specifying relation (expect intersects)
+        searchResponse = client().prepareSearch(defaultIndexName)
+            .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry))
             .get();
 
         assertSearchResponse(searchResponse);
@@ -92,4 +110,335 @@ public abstract class GeoQueryTests extends ESSingleNodeTestCase {
         assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
     }
 
+    public void testIndexPointsCircle() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate(defaultIndexName).setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field("name", "Document 1")
+            .field(defaultGeoFieldName, "POINT(-30 -30)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+            .field("name", "Document 2")
+            .field(defaultGeoFieldName, "POINT(-45 -50)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        CircleBuilder shape = new CircleBuilder().center(new Coordinate(-30, -30)).radius("100m");
+        GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(shape);
+        Geometry geometry = builder.buildGeometry().get(0);
+
+        try {
+            client().prepareSearch(defaultIndexName)
+                .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
+                    .relation(ShapeRelation.INTERSECTS))
+                .get();
+        } catch (
+            Exception e) {
+            assertThat(e.getCause().getMessage(),
+                containsString("failed to create query: "
+                    + GeoShapeType.CIRCLE + " geometry is not supported"));
+        }
+    }
+
+    public void testIndexPointsPolygon() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate(defaultIndexName).setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-30 -30)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-45 -50)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        CoordinatesBuilder cb = new CoordinatesBuilder();
+        cb.coordinate(new Coordinate(-35, -35))
+            .coordinate(new Coordinate(-35, -25))
+            .coordinate(new Coordinate(-25, -25))
+            .coordinate(new Coordinate(-25, -35))
+            .coordinate(new Coordinate(-35, -35));
+        PolygonBuilder shape = new PolygonBuilder(cb);
+        GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(shape);
+        Geometry geometry = builder.buildGeometry();
+        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+            .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, geometry)
+                .relation(ShapeRelation.INTERSECTS))
+            .get();
+
+        assertSearchResponse(searchResponse);
+        SearchHits searchHits = searchResponse.getHits();
+        assertThat(searchHits.getTotalHits().value, equalTo(1L));
+        assertThat(searchHits.getAt(0).getId(), equalTo("1"));
+    }
+
+    public void testIndexPointsMultiPolygon() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate(defaultIndexName).setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field("name", "Document 1")
+            .field(defaultGeoFieldName, "POINT(-30 -30)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+            .field("name", "Document 2")
+            .field(defaultGeoFieldName, "POINT(-40 -40)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("3").setSource(jsonBuilder()
+            .startObject()
+            .field("name", "Document 3")
+            .field(defaultGeoFieldName, "POINT(-50 -50)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        CoordinatesBuilder encloseDocument1Cb = new CoordinatesBuilder();
+        encloseDocument1Cb.coordinate(new Coordinate(-35, -35))
+            .coordinate(new Coordinate(-35, -25))
+            .coordinate(new Coordinate(-25, -25))
+            .coordinate(new Coordinate(-25, -35))
+            .coordinate(new Coordinate(-35, -35));
+        PolygonBuilder encloseDocument1Shape = new PolygonBuilder(encloseDocument1Cb);
+
+        CoordinatesBuilder encloseDocument2Cb = new CoordinatesBuilder();
+        encloseDocument2Cb.coordinate(new Coordinate(-55, -55))
+            .coordinate(new Coordinate(-55, -45))
+            .coordinate(new Coordinate(-45, -45))
+            .coordinate(new Coordinate(-45, -55))
+            .coordinate(new Coordinate(-55, -55));
+        PolygonBuilder encloseDocument2Shape = new PolygonBuilder(encloseDocument2Cb);
+
+        MultiPolygonBuilder mp = new MultiPolygonBuilder();
+        mp.polygon(encloseDocument1Shape).polygon(encloseDocument2Shape);
+
+        GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(mp);
+        Geometry geometry = builder.buildGeometry();
+        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")));
+    }
+
+    public void testIndexPointsRectangle() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate(defaultIndexName).setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field("name", "Document 1")
+            .field(defaultGeoFieldName, "POINT(-30 -30)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+            .field("name", "Document 2")
+            .field(defaultGeoFieldName, "POINT(-45 -50)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        Rectangle rectangle = new Rectangle(-50, -40, -45, -55);
+
+        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+            .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, rectangle)
+                .relation(ShapeRelation.INTERSECTS))
+            .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"));
+    }
+
+    public void testIndexPointsIndexedRectangle() throws Exception {
+        String mapping = Strings.toString(createDefaultMapping());
+        client().admin().indices().prepareCreate(defaultIndexName).setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("point1").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-30 -30)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("point2").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-45 -50)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        String indexedShapeIndex = "indexed_query_shapes";
+        String indexedShapePath = "shape";
+        String queryShapesMapping = Strings.toString(XContentFactory.jsonBuilder().startObject()
+            .startObject("properties").startObject(indexedShapePath)
+            .field("type", "geo_shape")
+            .endObject()
+            .endObject()
+            .endObject());
+        client().admin().indices().prepareCreate(indexedShapeIndex).setMapping(queryShapesMapping).get();
+        ensureGreen();
+
+        client().prepareIndex(indexedShapeIndex).setId("shape1").setSource(jsonBuilder()
+            .startObject()
+            .field(indexedShapePath, "BBOX(-50, -40, -45, -55)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(indexedShapeIndex).setId("shape2").setSource(jsonBuilder()
+            .startObject()
+            .field(indexedShapePath, "BBOX(-60, -50, -50, -60)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
+            .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, "shape1")
+                .relation(ShapeRelation.INTERSECTS)
+                .indexedShapeIndex(indexedShapeIndex)
+                .indexedShapePath(indexedShapePath))
+            .get();
+
+        assertSearchResponse(searchResponse);
+        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
+        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("point2"));
+
+        searchResponse = client().prepareSearch(defaultIndexName)
+            .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, "shape2")
+                .relation(ShapeRelation.INTERSECTS)
+                .indexedShapeIndex(indexedShapeIndex)
+                .indexedShapePath(indexedShapePath))
+            .get();
+        assertSearchResponse(searchResponse);
+        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L));
+    }
+
+    public void testRectangleSpanningDateline() throws Exception {
+        XContentBuilder mapping = createDefaultMapping();
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-169 0)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-179 0)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("3").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(171 0)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        Rectangle rectangle = new Rectangle(
+            169, -178, 1, -1);
+
+        GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("geo", rectangle);
+        SearchResponse response = client().prepareSearch("test").setQuery(geoShapeQueryBuilder).get();
+        SearchHits searchHits = response.getHits();
+        assertEquals(2, searchHits.getTotalHits().value);
+        assertNotEquals("1", searchHits.getAt(0).getId());
+        assertNotEquals("1", searchHits.getAt(1).getId());
+    }
+
+    public void testPolygonSpanningDateline() throws Exception {
+        XContentBuilder mapping = createDefaultMapping();
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-169 7)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-179 7)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("3").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(179 7)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("4").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(171 7)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        PolygonBuilder polygon = new PolygonBuilder(new CoordinatesBuilder()
+                    .coordinate(-177, 10)
+                    .coordinate(177, 10)
+                    .coordinate(177, 5)
+                    .coordinate(-177, 5)
+                    .coordinate(-177, 10));
+
+        GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("geo", polygon.buildGeometry());
+        geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
+        SearchResponse response = client().prepareSearch("test").setQuery(geoShapeQueryBuilder).get();
+        SearchHits searchHits = response.getHits();
+        assertEquals(2, searchHits.getTotalHits().value);
+        assertNotEquals("1", searchHits.getAt(0).getId());
+        assertNotEquals("4", searchHits.getAt(0).getId());
+        assertNotEquals("1", searchHits.getAt(1).getId());
+        assertNotEquals("4", searchHits.getAt(1).getId());
+    }
+
+    public void testMultiPolygonSpanningDateline() throws Exception {
+        XContentBuilder mapping = createDefaultMapping();
+        client().admin().indices().prepareCreate("test").setMapping(mapping).get();
+        ensureGreen();
+
+        client().prepareIndex(defaultIndexName).setId("1").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-169 7)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("2").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(-179 7)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        client().prepareIndex(defaultIndexName).setId("3").setSource(jsonBuilder()
+            .startObject()
+            .field(defaultGeoFieldName, "POINT(171 7)")
+            .endObject()).setRefreshPolicy(IMMEDIATE).get();
+
+        MultiPolygonBuilder multiPolygon = new MultiPolygonBuilder()
+            .polygon(new PolygonBuilder(new CoordinatesBuilder()
+                .coordinate(-167, 10)
+                .coordinate(-171, 10)
+                .coordinate(171, 5)
+                .coordinate(-167, 5)
+                .coordinate(-167, 10)))
+            .polygon(new PolygonBuilder(new CoordinatesBuilder()
+                .coordinate(-177, 10)
+                .coordinate(177, 10)
+                .coordinate(177, 5)
+                .coordinate(-177, 5)
+                .coordinate(-177, 10)));
+
+        GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery(
+            "geo",
+            multiPolygon.buildGeometry());
+        geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
+        SearchResponse response = client().prepareSearch("test").setQuery(geoShapeQueryBuilder).get();
+        SearchHits searchHits = response.getHits();
+        assertEquals(2, searchHits.getTotalHits().value);
+        assertNotEquals("3", searchHits.getAt(0).getId());
+        assertNotEquals("3", searchHits.getAt(1).getId());
+    }
 }

+ 0 - 4
server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java

@@ -109,10 +109,6 @@ public class GeoShapeQueryTests extends GeoQueryTests {
         return xcb;
     }
 
-    public void testIndexPointsFilterRectangle() throws Exception {
-        super.testIndexPointsFilterRectangle(Strings.toString(createRandomMapping()));
-    }
-
     public void testShapeFetchingPath() throws Exception {
         createIndex("shapes");
         String mapping = Strings.toString(createDefaultMapping());

+ 10 - 9
x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryBuilder.java

@@ -23,6 +23,7 @@ import org.elasticsearch.xpack.spatial.index.mapper.ShapeFieldMapper;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Supplier;
@@ -35,6 +36,9 @@ import java.util.function.Supplier;
 public class ShapeQueryBuilder extends AbstractGeometryQueryBuilder<ShapeQueryBuilder> {
     public static final String NAME = "shape";
 
+    protected static final List<String> validContentTypes =
+        Collections.unmodifiableList(Arrays.asList(ShapeFieldMapper.CONTENT_TYPE));
+
     /**
      * Creates a new GeoShapeQueryBuilder whose Query will be against the given
      * field name using the given Shape
@@ -100,23 +104,20 @@ public class ShapeQueryBuilder extends AbstractGeometryQueryBuilder<ShapeQueryBu
         return new ShapeQueryBuilder(fieldName, shapeSupplier, indexedShapeId);
     }
 
-    @Override
-    public String queryFieldType() {
-        return ShapeFieldMapper.CONTENT_TYPE;
-    }
-
     @Override
     @SuppressWarnings({ "rawtypes" })
-    protected List validContentTypes() {
-        return Arrays.asList(ShapeFieldMapper.CONTENT_TYPE);
+    protected List<String> validContentTypes(){
+        return validContentTypes;
     }
 
     @Override
     @SuppressWarnings({ "rawtypes" })
     public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) {
-        if (fieldType.typeName().equals(ShapeFieldMapper.CONTENT_TYPE) == false) {
+        List<String> validContentTypes = validContentTypes();
+        if (validContentTypes.contains(fieldType.typeName()) == false) {
             throw new QueryShardException(context,
-                "Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]");
+                "Field [" + fieldName + "] is not of type [" + String.join(" or ", validContentTypes())
+                    + "] but of type [" + fieldType.typeName() + "]");
         }
 
         final AbstractGeometryFieldMapper.AbstractGeometryFieldType ft = (AbstractGeometryFieldMapper.AbstractGeometryFieldType) fieldType;

+ 2 - 2
x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryProcessor.java

@@ -28,7 +28,7 @@ import org.elasticsearch.geometry.MultiPolygon;
 import org.elasticsearch.geometry.Point;
 import org.elasticsearch.geometry.Polygon;
 import org.elasticsearch.geometry.Rectangle;
-import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
+import org.elasticsearch.index.mapper.AbstractSearchableGeometryFieldType;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.query.QueryShardException;
@@ -36,7 +36,7 @@ import org.elasticsearch.xpack.spatial.index.mapper.ShapeFieldMapper;
 
 import static org.elasticsearch.xpack.spatial.index.mapper.ShapeIndexer.toLucenePolygon;
 
-public class ShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
+public class ShapeQueryProcessor implements AbstractSearchableGeometryFieldType.QueryProcessor {
 
     @Override
     public Query process(Geometry shape, String fieldName, ShapeRelation relation, QueryShardContext context) {