Browse Source

Revert "[Geo] Integrate Lucene's LatLonShape (BKD Backed GeoShapes) as default `geo_shape` indexing approach (#35320)"

This reverts commit 5bc7822562a6eefa4a64743233160cdc9f431adf.
Nicholas Knize 6 years ago
parent
commit
96d279ed83
31 changed files with 1233 additions and 2635 deletions
  1. 76 110
      docs/reference/mapping/types/geo-shape.asciidoc
  2. 0 16
      docs/reference/migration/migrate_7_0/mappings.asciidoc
  3. 2 3
      docs/reference/query-dsl/geo-shape-query.asciidoc
  4. 0 12
      server/src/main/java/org/elasticsearch/common/geo/ShapeRelation.java
  5. 3 0
      server/src/main/java/org/elasticsearch/common/geo/builders/GeometryCollectionBuilder.java
  6. 9 15
      server/src/main/java/org/elasticsearch/common/geo/parsers/GeoJsonParser.java
  7. 6 7
      server/src/main/java/org/elasticsearch/common/geo/parsers/GeoWKTParser.java
  8. 2 2
      server/src/main/java/org/elasticsearch/common/geo/parsers/ShapeParser.java
  9. 0 336
      server/src/main/java/org/elasticsearch/index/mapper/BaseGeoShapeFieldMapper.java
  10. 530 80
      server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java
  11. 0 596
      server/src/main/java/org/elasticsearch/index/mapper/LegacyGeoShapeFieldMapper.java
  12. 25 92
      server/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java
  13. 6 2
      server/src/main/java/org/elasticsearch/indices/IndicesModule.java
  14. 5 3
      server/src/test/java/org/elasticsearch/common/geo/GeoJsonShapeParserTests.java
  15. 7 12
      server/src/test/java/org/elasticsearch/common/geo/GeoWKTShapeParserTests.java
  16. 7 14
      server/src/test/java/org/elasticsearch/index/mapper/ExternalMapper.java
  17. 2 4
      server/src/test/java/org/elasticsearch/index/mapper/ExternalValuesMapperIntegrationIT.java
  18. 410 42
      server/src/test/java/org/elasticsearch/index/mapper/GeoShapeFieldMapperTests.java
  19. 49 3
      server/src/test/java/org/elasticsearch/index/mapper/GeoShapeFieldTypeTests.java
  20. 0 714
      server/src/test/java/org/elasticsearch/index/mapper/LegacyGeoShapeFieldMapperTests.java
  21. 0 86
      server/src/test/java/org/elasticsearch/index/mapper/LegacyGeoShapeFieldTypeTests.java
  22. 41 34
      server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java
  23. 0 94
      server/src/test/java/org/elasticsearch/index/query/LegacyGeoShapeFieldQueryTests.java
  24. 0 1
      server/src/test/java/org/elasticsearch/index/query/MatchQueryBuilderTests.java
  25. 0 6
      server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java
  26. 0 1
      server/src/test/java/org/elasticsearch/search/geo/GeoFilterIT.java
  27. 13 12
      server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java
  28. 39 147
      server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java
  29. 0 170
      server/src/test/java/org/elasticsearch/search/geo/LegacyGeoShapeIntegrationIT.java
  30. 0 2
      server/src/test/java/org/elasticsearch/test/geo/RandomShapeGenerator.java
  31. 1 19
      test/framework/src/main/java/org/elasticsearch/test/AbstractBuilderTestCase.java

+ 76 - 110
docs/reference/mapping/types/geo-shape.asciidoc

@@ -21,59 +21,48 @@ type.
 |=======================================================================
 |Option |Description| Default
 
-|`tree |deprecated[6.6, PrefixTrees no longer used] Name of the PrefixTree
-implementation to be used: `geohash` for GeohashPrefixTree and `quadtree`
-for QuadPrefixTree. Note: This parameter is only relevant for `term` and
-`recursive` strategies.
-| `quadtree`
-
-|`precision` |deprecated[6.6, PrefixTrees no longer used] This parameter may
-be used instead of `tree_levels` to set an appropriate value for the
-`tree_levels` parameter. The value specifies the desired precision and
-Elasticsearch will calculate the best tree_levels value to honor this
-precision. The value should be a number followed by an optional distance
-unit. Valid distance units include: `in`, `inch`, `yd`, `yard`, `mi`,
-`miles`, `km`, `kilometers`, `m`,`meters`, `cm`,`centimeters`, `mm`,
-`millimeters`. Note: This parameter is only relevant for `term` and
-`recursive` strategies.
+|`tree` |Name of the PrefixTree implementation to be used: `geohash` for
+GeohashPrefixTree and `quadtree` for QuadPrefixTree.
+| `geohash`
+
+|`precision` |This parameter may be used instead of `tree_levels` to set
+an appropriate value for the `tree_levels` parameter. The value
+specifies the desired precision and Elasticsearch will calculate the
+best tree_levels value to honor this precision. The value should be a
+number followed by an optional distance unit. Valid distance units
+include: `in`, `inch`, `yd`, `yard`, `mi`, `miles`, `km`, `kilometers`,
+`m`,`meters`, `cm`,`centimeters`, `mm`, `millimeters`.
 | `50m`
 
-|`tree_levels` |deprecated[6.6, PrefixTrees no longer used] Maximum number
-of layers to be used by the PrefixTree. This can be used to control the
-precision of shape representations andtherefore how many terms are
-indexed. Defaults to the default value of the chosen PrefixTree
-implementation. Since this parameter requires a certain level of
-understanding of the underlying implementation, users may use the
-`precision` parameter instead. However, Elasticsearch only uses the
-tree_levels parameter internally and this is what is returned via the
-mapping API even if you use the precision parameter. Note: This parameter
-is only relevant for `term` and `recursive` strategies.
+|`tree_levels` |Maximum number of layers to be used by the PrefixTree.
+This can be used to control the precision of shape representations and
+therefore how many terms are indexed. Defaults to the default value of
+the chosen PrefixTree implementation. Since this parameter requires a
+certain level of understanding of the underlying implementation, users
+may use the `precision` parameter instead. However, Elasticsearch only
+uses the tree_levels parameter internally and this is what is returned
+via the mapping API even if you use the precision parameter.
 | various
 
-|`strategy` |deprecated[6.6, PrefixTrees no longer used] The strategy
-parameter defines the approach for how to represent shapes at indexing
-and search time. It also influences the capabilities available so it
-is recommended to let Elasticsearch set this parameter automatically.
-There are two strategies available: `recursive`, and `term`.
-Recursive and Term strategies are deprecated and will be removed in a
-future version. While they are still available, the Term strategy
-supports point types only (the `points_only` parameter will be
-automatically set to true) while Recursive strategy supports all
-shape types. (IMPORTANT: see <<prefix-trees, Prefix trees>> for more
-detailed information about these strategies)
+|`strategy` |The strategy parameter defines the approach for how to
+represent shapes at indexing and search time. It also influences the
+capabilities available so it is recommended to let Elasticsearch set
+this parameter automatically. There are two strategies available:
+`recursive` and `term`. Term strategy supports point types only (the
+`points_only` parameter will be automatically set to true) while
+Recursive strategy supports all shape types. (IMPORTANT: see
+<<prefix-trees, Prefix trees>> for more detailed information)
 | `recursive`
 
-|`distance_error_pct` |deprecated[6.6, PrefixTrees no longer used] Used as a
-hint to the PrefixTree about how precise it should be. Defaults to 0.025 (2.5%)
-with 0.5 as the maximum supported value. PERFORMANCE NOTE: This value will
-default to 0 if a `precision` or `tree_level` definition is explicitly defined.
-This guarantees spatial precision at the level defined in the mapping. This can
-lead to significant memory usage for high resolution shapes with low error
-(e.g., large shapes at 1m with < 0.001 error). To improve indexing performance
-(at the cost of query accuracy) explicitly define `tree_level` or `precision`
-along with a reasonable `distance_error_pct`, noting that large shapes will have
-greater false positives. Note: This parameter is only relevant for `term` and
-`recursive` strategies.
+|`distance_error_pct` |Used as a hint to the PrefixTree about how
+precise it should be. Defaults to 0.025 (2.5%) with 0.5 as the maximum
+supported value. PERFORMANCE NOTE: This value will default to 0 if a `precision` or
+`tree_level` definition is explicitly defined. This guarantees spatial precision
+at the level defined in the mapping. This can lead to significant memory usage
+for high resolution shapes with low error (e.g., large shapes at 1m with < 0.001 error).
+To improve indexing performance (at the cost of query accuracy) explicitly define
+`tree_level` or `precision` along with a reasonable `distance_error_pct`, noting
+that large shapes will have greater false positives.
 | `0.025`
 
 |`orientation` |Optionally define how to interpret vertex order for
@@ -88,13 +77,13 @@ sets vertex order for the coordinate list of a geo_shape field but can be
 overridden in each individual GeoJSON or WKT document.
 | `ccw`
 
-|`points_only` |deprecated[6.6, PrefixTrees no longer used] Setting this option to
-`true` (defaults to `false`) configures the `geo_shape` field type for point
-shapes only (NOTE: Multi-Points are not yet supported). This optimizes index and
-search performance for the `geohash` and `quadtree` when it is known that only points
-will be indexed. At present geo_shape queries can not be executed on `geo_point`
-field types. This option bridges the gap by improving point performance on a
-`geo_shape` field so that `geo_shape` queries are optimal on a point only field.
+|`points_only` |Setting this option to `true` (defaults to `false`) configures
+the `geo_shape` field type for point shapes only (NOTE: Multi-Points are not
+yet supported). This optimizes index and search performance for the `geohash` and
+`quadtree` when it is known that only points will be indexed. At present geo_shape
+queries can not be executed on `geo_point` field types. This option bridges the gap
+by improving point performance on a `geo_shape` field so that `geo_shape` queries are
+optimal on a point only field.
 | `false`
 
 |`ignore_malformed` |If true, malformed GeoJSON or WKT shapes are ignored. If
@@ -111,35 +100,16 @@ and reject the whole document.
 
 |=======================================================================
 
-
-[[geoshape-indexing-approach]]
-[float]
-==== Indexing approach
-GeoShape types are indexed by decomposing the shape into a triangular mesh and
-indexing each triangle as a 7 dimension point in a BKD tree. This provides
-near perfect spatial resolution (down to 1e-7 decimal degree precision) since all
-spatial relations are computed using an encoded vector representation of the
-original shape instead of a raster-grid representation as used by the
-<<prefix-trees>> indexing approach. Performance of the tessellator primarily
-depends on the number of vertices that define the polygon/multi-polyogn. While
-this is the default indexing technique prefix trees can still be used by setting
-the `tree` or `strategy` parameters according to the appropriate
-<<geo-shape-mapping-options>>. Note that these parameters are now deprecated
-and will be removed in a future version.
-
 [[prefix-trees]]
 [float]
 ==== Prefix trees
 
-deprecated[6.6, PrefixTrees no longer used] To efficiently represent shapes in
-an inverted index, Shapes are converted into a series of hashes representing
-grid squares (commonly referred to as "rasters") using implementations of a
-PrefixTree. The tree notion comes from the fact that the PrefixTree uses multiple
-grid layers, each with an increasing level of precision to represent the Earth.
-This can be thought of as increasing the level of detail of a map or image at higher
-zoom levels. Since this approach causes precision issues with indexed shape, it has
-been deprecated in favor of a vector indexing approach that indexes the shapes as a
-triangular mesh (see <<geoshape-indexing-approach>>).
+To efficiently represent shapes in the index, Shapes are converted into
+a series of hashes representing grid squares (commonly referred to as "rasters")
+using implementations of a PrefixTree. The tree notion comes from the fact that
+the PrefixTree uses multiple grid layers, each with an increasing level of
+precision to represent the Earth. This can be thought of as increasing the level
+of detail of a map or image at higher zoom levels.
 
 Multiple PrefixTree implementations are provided:
 
@@ -161,10 +131,9 @@ number of levels for the quad trees in Elasticsearch is 29; the default is 21.
 [[spatial-strategy]]
 [float]
 ===== Spatial strategies
-deprecated[6.6, PrefixTrees no longer used]  The indexing implementation
-selected relies on a  SpatialStrategy for choosing how to decompose the shapes
-(either as grid  squares or a tessellated triangular mesh). Each strategy
-answers the following:
+The PrefixTree implementations rely on a SpatialStrategy for decomposing
+the provided Shape(s) into approximated grid squares. Each strategy answers
+the following:
 
 * What type of Shapes can be indexed?
 * What types of Query Operations and Shapes can be used?
@@ -177,7 +146,7 @@ are provided:
 |=======================================================================
 |Strategy |Supported Shapes |Supported Queries |Multiple Shapes
 
-|`recursive`  |<<input-structure, All>> |`INTERSECTS`, `DISJOINT`, `WITHIN`, `CONTAINS` |Yes
+|`recursive` |<<input-structure, All>> |`INTERSECTS`, `DISJOINT`, `WITHIN`, `CONTAINS` |Yes
 |`term` |<<point, Points>> |`INTERSECTS` |Yes
 
 |=======================================================================
@@ -185,13 +154,13 @@ are provided:
 [float]
 ===== Accuracy
 
-`Recursive` and `Term` strategies do not provide 100% accuracy and depending on
-how they are configured it may return some false positives for `INTERSECTS`,
-`WITHIN` and `CONTAINS` queries, and some false negatives for `DISJOINT` queries.
-To mitigate this, it is important to select an appropriate value for the tree_levels
-parameter and to adjust expectations accordingly. For example, a point may be near
-the border of a particular grid cell and may thus not match a query that only matches
-the cell right next to it -- even though the shape is very close to the point.
+Geo_shape does not provide 100% accuracy and depending on how it is configured
+it may return some false positives for `INTERSECTS`, `WITHIN` and `CONTAINS`
+queries, and some false negatives for `DISJOINT` queries. To mitigate this, it
+is important to select an appropriate value for the tree_levels parameter and
+to adjust expectations accordingly. For example, a point may be near the border
+of a particular grid cell and may thus not match a query that only matches the
+cell right next to it -- even though the shape is very close to the point.
 
 [float]
 ===== Example
@@ -204,7 +173,9 @@ PUT /example
         "doc": {
             "properties": {
                 "location": {
-                    "type": "geo_shape"
+                    "type": "geo_shape",
+                    "tree": "quadtree",
+                    "precision": "100m"
                 }
             }
         }
@@ -214,23 +185,22 @@ PUT /example
 // CONSOLE
 // TESTSETUP
 
-This mapping definition maps the location field to the geo_shape
-type using the default vector implementation. It provides
-approximately 1e-7 decimal degree precision.
+This mapping maps the location field to the geo_shape type using the
+quad_tree implementation and a precision of 100m. Elasticsearch translates
+this into a tree_levels setting of 20.
 
 [float]
-===== Performance considerations with Prefix Trees
+===== Performance considerations
 
-deprecated[6.6, PrefixTrees no longer used] With prefix trees,
-Elasticsearch uses the paths in the tree as terms in the inverted index
-and in queries. The higher the level (and thus the precision), the more
-terms are generated. Of course, calculating the terms, keeping them in
+Elasticsearch uses the paths in the prefix tree as terms in the index
+and in queries. The higher the level is (and thus the precision), the
+more terms are generated. Of course, calculating the terms, keeping them in
 memory, and storing them on disk all have a price. Especially with higher
-tree levels, indices can become extremely large even with a modest amount
-of data. Additionally, the size of the features also matters. Big, complex
-polygons can take up a lot of space at higher tree levels. Which setting
-is right depends on the use case. Generally one trades off accuracy against
-index size and query performance.
+tree levels, indices can become extremely large even with a modest
+amount of data. Additionally, the size of the features also matters.
+Big, complex polygons can take up a lot of space at higher tree levels.
+Which setting is right depends on the use case. Generally one trades off
+accuracy against index size and query performance.
 
 The defaults in Elasticsearch for both implementations are a compromise
 between index size and a reasonable level of precision of 50m at the
@@ -628,10 +598,7 @@ POST /example/doc
 ===== Circle
 
 Elasticsearch supports a `circle` type, which consists of a center
-point with a radius. Note that this circle representation can only
-be indexed when using the `recursive` Prefix Tree strategy. For
-the default <<geoshape-indexing-approach>> circles should be approximated using
-a `POLYGON`.
+point with a radius:
 
 [source,js]
 --------------------------------------------------
@@ -645,7 +612,6 @@ POST /example/doc
 }
 --------------------------------------------------
 // CONSOLE
-// TEST[skip:not supported in default]
 
 Note: The inner `radius` field is required. If not specified, then
 the units of the `radius` will default to `METERS`.

+ 0 - 16
docs/reference/migration/migrate_7_0/mappings.asciidoc

@@ -52,19 +52,3 @@ as a better alternative.
 
 An error will now be thrown when unknown configuration options are provided
 to similarities. Such unknown parameters were ignored before.
-
-[float]
-==== deprecated `geo_shape` Prefix Tree indexing
-
-`geo_shape` types now default to using a vector indexing approach based on Lucene's new
-`LatLonShape` field type. This indexes shapes as a triangular mesh instead of decomposing
-them into individual grid cells. To index using legacy prefix trees `recursive` or `term`
-strategy must be explicitly defined. Note that these strategies are now deprecated and will
-be removed in a future version.
-
-[float]
-==== deprecated `geo_shape` parameters
-
-The following type parameters are deprecated for the `geo_shape` field type: `tree`,
-`precision`, `tree_levels`, `distance_error_pct`, `points_only`, and `strategy`. They
-will be removed in a future version.

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

@@ -7,7 +7,7 @@ Requires the <<geo-shape,`geo_shape` 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
-with the query shape. It will also use the same Prefix Tree configuration
+with the query shape. It will also use the same PrefixTree configuration
 as defined for the field mapping.
 
 The query supports two ways of defining the query shape, either by
@@ -157,8 +157,7 @@ has nothing in common with the query geometry.
 * `WITHIN` - Return all documents whose `geo_shape` field
 is within the query geometry.
 * `CONTAINS` - Return all documents whose `geo_shape` field
-contains the query geometry. Note: this is only supported using the
-`recursive` Prefix Tree Strategy deprecated[6.6]
+contains the query geometry.
 
 [float]
 ==== Ignore Unmapped

+ 0 - 12
server/src/main/java/org/elasticsearch/common/geo/ShapeRelation.java

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.common.geo;
 
-import org.apache.lucene.document.LatLonShape.QueryRelation;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.io.stream.Writeable;
@@ -63,17 +62,6 @@ public enum ShapeRelation implements Writeable {
         return null;
     }
 
-    /** Maps ShapeRelation to Lucene's LatLonShapeRelation */
-    public QueryRelation getLuceneRelation() {
-        switch (this) {
-            case INTERSECTS: return QueryRelation.INTERSECTS;
-            case DISJOINT: return QueryRelation.DISJOINT;
-            case WITHIN: return QueryRelation.WITHIN;
-            default:
-                throw new IllegalArgumentException("ShapeRelation [" + this + "] not supported");
-        }
-    }
-
     public String getRelationName() {
         return relationName;
     }

+ 3 - 0
server/src/main/java/org/elasticsearch/common/geo/builders/GeometryCollectionBuilder.java

@@ -197,6 +197,9 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape, GeometryColle
             }
         }
 
+        if (shapes.size() == 1) {
+            return shapes.get(0);
+        }
         return shapes.toArray(new Object[shapes.size()]);
     }
 

+ 9 - 15
server/src/main/java/org/elasticsearch/common/geo/parsers/GeoJsonParser.java

@@ -25,11 +25,10 @@ import org.elasticsearch.common.geo.GeoShapeType;
 import org.elasticsearch.common.geo.builders.CircleBuilder;
 import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
-import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
 import org.elasticsearch.common.unit.DistanceUnit;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentSubParser;
-import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.locationtech.jts.geom.Coordinate;
 
 import java.io.IOException;
@@ -42,22 +41,17 @@ import java.util.List;
  * complies with geojson specification: https://tools.ietf.org/html/rfc7946
  */
 abstract class GeoJsonParser {
-    protected static ShapeBuilder parse(XContentParser parser, BaseGeoShapeFieldMapper shapeMapper)
+    protected static ShapeBuilder parse(XContentParser parser, GeoShapeFieldMapper shapeMapper)
         throws IOException {
         GeoShapeType shapeType = null;
         DistanceUnit.Distance radius = null;
         CoordinateNode coordinateNode = null;
         GeometryCollectionBuilder geometryCollections = null;
 
-        Orientation orientation = (shapeMapper == null)
-            ? BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value()
-            : shapeMapper.orientation();
-        Explicit<Boolean> coerce = (shapeMapper == null)
-            ? BaseGeoShapeFieldMapper.Defaults.COERCE
-            : shapeMapper.coerce();
-        Explicit<Boolean> ignoreZValue = (shapeMapper == null)
-            ? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE
-            : shapeMapper.ignoreZValue();
+        ShapeBuilder.Orientation requestedOrientation =
+            (shapeMapper == null) ? ShapeBuilder.Orientation.RIGHT : shapeMapper.fieldType().orientation();
+        Explicit<Boolean> coerce = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
+        Explicit<Boolean> ignoreZValue = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE : shapeMapper.ignoreZValue();
 
         String malformedException = null;
 
@@ -108,7 +102,7 @@ abstract class GeoJsonParser {
                             malformedException = "cannot have [" + ShapeParser.FIELD_ORIENTATION + "] with type set to [" + shapeType + "]";
                         }
                         subParser.nextToken();
-                        orientation = ShapeBuilder.Orientation.fromString(subParser.text());
+                        requestedOrientation = ShapeBuilder.Orientation.fromString(subParser.text());
                     } else {
                         subParser.nextToken();
                         subParser.skipChildren();
@@ -134,7 +128,7 @@ abstract class GeoJsonParser {
             return geometryCollections;
         }
 
-        return shapeType.getBuilder(coordinateNode, radius, orientation, coerce.value());
+        return shapeType.getBuilder(coordinateNode, radius, requestedOrientation, coerce.value());
     }
 
     /**
@@ -208,7 +202,7 @@ abstract class GeoJsonParser {
      * @return Geometry[] geometries of the GeometryCollection
      * @throws IOException Thrown if an error occurs while reading from the XContentParser
      */
-    static GeometryCollectionBuilder parseGeometries(XContentParser parser, BaseGeoShapeFieldMapper mapper) throws
+    static GeometryCollectionBuilder parseGeometries(XContentParser parser, GeoShapeFieldMapper mapper) throws
         IOException {
         if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
             throw new ElasticsearchParseException("geometries must be an array of geojson objects");

+ 6 - 7
server/src/main/java/org/elasticsearch/common/geo/parsers/GeoWKTParser.java

@@ -34,7 +34,7 @@ import org.elasticsearch.common.geo.builders.PolygonBuilder;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.logging.Loggers;
 import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.locationtech.jts.geom.Coordinate;
 
 import java.io.IOException;
@@ -63,7 +63,7 @@ public class GeoWKTParser {
     // no instance
     private GeoWKTParser() {}
 
-    public static ShapeBuilder parse(XContentParser parser, final BaseGeoShapeFieldMapper shapeMapper)
+    public static ShapeBuilder parse(XContentParser parser, final GeoShapeFieldMapper shapeMapper)
             throws IOException, ElasticsearchParseException {
         return parseExpectedType(parser, null, shapeMapper);
     }
@@ -75,12 +75,12 @@ public class GeoWKTParser {
 
     /** throws an exception if the parsed geometry type does not match the expected shape type */
     public static ShapeBuilder parseExpectedType(XContentParser parser, final GeoShapeType shapeType,
-                                                 final BaseGeoShapeFieldMapper shapeMapper)
+                                                 final GeoShapeFieldMapper shapeMapper)
             throws IOException, ElasticsearchParseException {
         try (StringReader reader = new StringReader(parser.text())) {
-            Explicit<Boolean> ignoreZValue = (shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE :
+            Explicit<Boolean> ignoreZValue = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE :
                 shapeMapper.ignoreZValue();
-            Explicit<Boolean> coerce = (shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
+            Explicit<Boolean> coerce = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
             // setup the tokenizer; configured to read words w/o numbers
             StreamTokenizer tokenizer = new StreamTokenizer(reader);
             tokenizer.resetSyntax();
@@ -257,8 +257,7 @@ public class GeoWKTParser {
         if (nextEmptyOrOpen(stream).equals(EMPTY)) {
             return null;
         }
-        PolygonBuilder builder = new PolygonBuilder(parseLinearRing(stream, ignoreZValue, coerce),
-            BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value());
+        PolygonBuilder builder = new PolygonBuilder(parseLinearRing(stream, ignoreZValue, coerce), ShapeBuilder.Orientation.RIGHT);
         while (nextCloserOrComma(stream).equals(COMMA)) {
             builder.hole(parseLinearRing(stream, ignoreZValue, coerce));
         }

+ 2 - 2
server/src/main/java/org/elasticsearch/common/geo/parsers/ShapeParser.java

@@ -23,7 +23,7 @@ import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.xcontent.XContent;
 import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 
 import java.io.IOException;
 
@@ -46,7 +46,7 @@ public interface ShapeParser {
      *          if the parsers current token has been <code>null</code>
      * @throws IOException if the input could not be read
      */
-    static ShapeBuilder parse(XContentParser parser, BaseGeoShapeFieldMapper shapeMapper) throws IOException {
+    static ShapeBuilder parse(XContentParser parser, GeoShapeFieldMapper shapeMapper) throws IOException {
         if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
             return null;
         } if (parser.currentToken() == XContentParser.Token.START_OBJECT) {

+ 0 - 336
server/src/main/java/org/elasticsearch/index/mapper/BaseGeoShapeFieldMapper.java

@@ -1,336 +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.mapper;
-
-import org.apache.lucene.index.IndexOptions;
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.Query;
-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.builders.ShapeBuilder;
-import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.support.XContentMapValues;
-import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.DeprecatedParameters;
-import org.elasticsearch.index.query.QueryShardContext;
-import org.elasticsearch.index.query.QueryShardException;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_MALFORMED;
-
-/**
- * Base class for {@link GeoShapeFieldMapper} and {@link LegacyGeoShapeFieldMapper}
- */
-public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
-    public static final String CONTENT_TYPE = "geo_shape";
-
-    public static class Names {
-        public static final ParseField ORIENTATION = new ParseField("orientation");
-        public static final ParseField COERCE = new ParseField("coerce");
-    }
-
-    public static class Defaults {
-        public static final Explicit<Orientation> ORIENTATION = new Explicit<>(Orientation.RIGHT, false);
-        public static final Explicit<Boolean> COERCE = new Explicit<>(false, false);
-        public static final Explicit<Boolean> IGNORE_MALFORMED = new Explicit<>(false, false);
-        public static final Explicit<Boolean> IGNORE_Z_VALUE = new Explicit<>(true, false);
-    }
-
-    public abstract static class Builder<T extends Builder, Y extends BaseGeoShapeFieldMapper>
-            extends FieldMapper.Builder<T, Y> {
-        protected Boolean coerce;
-        protected Boolean ignoreMalformed;
-        protected Boolean ignoreZValue;
-        protected Orientation orientation;
-
-        /** default builder - used for external mapper*/
-        public Builder(String name, MappedFieldType fieldType, MappedFieldType defaultFieldType) {
-            super(name, fieldType, defaultFieldType);
-        }
-
-        public Builder(String name, MappedFieldType fieldType, MappedFieldType defaultFieldType,
-                       boolean coerce, boolean ignoreMalformed, Orientation orientation, boolean ignoreZ) {
-            super(name, fieldType, defaultFieldType);
-            this.coerce = coerce;
-            this.ignoreMalformed = ignoreMalformed;
-            this.orientation = orientation;
-            this.ignoreZValue = ignoreZ;
-        }
-
-        public Builder coerce(boolean coerce) {
-            this.coerce = coerce;
-            return this;
-        }
-
-        protected Explicit<Boolean> coerce(BuilderContext context) {
-            if (coerce != null) {
-                return new Explicit<>(coerce, true);
-            }
-            if (context.indexSettings() != null) {
-                return new Explicit<>(COERCE_SETTING.get(context.indexSettings()), false);
-            }
-            return Defaults.COERCE;
-        }
-
-        public Builder orientation(Orientation orientation) {
-            this.orientation = orientation;
-            return this;
-        }
-
-        protected Explicit<Orientation> orientation() {
-            if (orientation != null) {
-                return new Explicit<>(orientation, true);
-            }
-            return Defaults.ORIENTATION;
-        }
-
-        @Override
-        protected boolean defaultDocValues(Version indexCreated) {
-            return false;
-        }
-
-        public Builder ignoreMalformed(boolean ignoreMalformed) {
-            this.ignoreMalformed = ignoreMalformed;
-            return this;
-        }
-
-        protected Explicit<Boolean> ignoreMalformed(BuilderContext context) {
-            if (ignoreMalformed != null) {
-                return new Explicit<>(ignoreMalformed, true);
-            }
-            if (context.indexSettings() != null) {
-                return new Explicit<>(IGNORE_MALFORMED_SETTING.get(context.indexSettings()), false);
-            }
-            return Defaults.IGNORE_MALFORMED;
-        }
-
-        protected Explicit<Boolean> ignoreZValue() {
-            if (ignoreZValue != null) {
-                return new Explicit<>(ignoreZValue, true);
-            }
-            return Defaults.IGNORE_Z_VALUE;
-        }
-
-        public Builder ignoreZValue(final boolean ignoreZValue) {
-            this.ignoreZValue = ignoreZValue;
-            return this;
-        }
-
-        @Override
-        protected void setupFieldType(BuilderContext context) {
-            super.setupFieldType(context);
-
-            // field mapper handles this at build time
-            // but prefix tree strategies require a name, so throw a similar exception
-            if (name().isEmpty()) {
-                throw new IllegalArgumentException("name cannot be empty string");
-            }
-
-            BaseGeoShapeFieldType ft = (BaseGeoShapeFieldType)fieldType();
-            ft.setOrientation(orientation().value());
-        }
-    }
-
-    public static class TypeParser implements Mapper.TypeParser {
-
-        @Override
-        public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
-            boolean coerce = Defaults.COERCE.value();
-            boolean ignoreZ = Defaults.IGNORE_Z_VALUE.value();
-            boolean ignoreMalformed = Defaults.IGNORE_MALFORMED.value();
-            Orientation orientation = Defaults.ORIENTATION.value();
-            DeprecatedParameters deprecatedParameters = new DeprecatedParameters();
-            boolean parsedDeprecatedParams = false;
-            for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
-                Map.Entry<String, Object> entry = iterator.next();
-                String fieldName = entry.getKey();
-                Object fieldNode = entry.getValue();
-                if (DeprecatedParameters.parse(name, fieldName, fieldNode, deprecatedParameters)) {
-                    parsedDeprecatedParams = true;
-                    iterator.remove();
-                } else if (Names.ORIENTATION.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                    orientation = ShapeBuilder.Orientation.fromString(fieldNode.toString());
-                    iterator.remove();
-                } else if (IGNORE_MALFORMED.equals(fieldName)) {
-                    ignoreMalformed = XContentMapValues.nodeBooleanValue(fieldNode, name + ".ignore_malformed");
-                    iterator.remove();
-                } else if (Names.COERCE.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                    coerce = XContentMapValues.nodeBooleanValue(fieldNode, name + "." + Names.COERCE.getPreferredName());
-                    iterator.remove();
-                } else if (GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName().equals(fieldName)) {
-                    ignoreZ = XContentMapValues.nodeBooleanValue(fieldNode,
-                        name + "." + GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName());
-                    iterator.remove();
-                }
-            }
-            return getBuilder(name, coerce, ignoreMalformed, orientation, ignoreZ, parsedDeprecatedParams ? deprecatedParameters : null);
-        }
-
-        private Builder getBuilder(String name, boolean coerce, boolean ignoreMalformed, Orientation orientation,
-                                   boolean ignoreZ, DeprecatedParameters deprecatedParameters) {
-            if (deprecatedParameters != null) {
-                return getLegacyBuilder(name, coerce, ignoreMalformed, orientation, ignoreZ, deprecatedParameters);
-            }
-            return new GeoShapeFieldMapper.Builder(name, coerce, ignoreMalformed, orientation, ignoreZ);
-        }
-
-        private Builder getLegacyBuilder(String name, boolean coerce, boolean ignoreMalformed, Orientation orientation,
-                                         boolean ignoreZ, DeprecatedParameters deprecatedParameters) {
-            return new LegacyGeoShapeFieldMapper.Builder(name, coerce, ignoreMalformed, orientation, ignoreZ, deprecatedParameters);
-        }
-    }
-
-    public abstract static class BaseGeoShapeFieldType extends MappedFieldType {
-        protected Orientation orientation = Defaults.ORIENTATION.value();
-
-        protected BaseGeoShapeFieldType() {
-            setIndexOptions(IndexOptions.DOCS);
-            setTokenized(false);
-            setStored(false);
-            setStoreTermVectors(false);
-            setOmitNorms(true);
-        }
-
-        protected BaseGeoShapeFieldType(BaseGeoShapeFieldType ref) {
-            super(ref);
-            this.orientation = ref.orientation;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (!super.equals(o)) return false;
-            BaseGeoShapeFieldType that = (BaseGeoShapeFieldType) o;
-            return orientation == that.orientation;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(super.hashCode(), orientation);
-        }
-
-        @Override
-        public String typeName() {
-            return CONTENT_TYPE;
-        }
-
-        @Override
-        public void checkCompatibility(MappedFieldType fieldType, List<String> conflicts) {
-            super.checkCompatibility(fieldType, conflicts);
-        }
-
-        public Orientation orientation() { return this.orientation; }
-
-        public void setOrientation(Orientation orientation) {
-            checkIfFrozen();
-            this.orientation = orientation;
-        }
-
-        @Override
-        public Query existsQuery(QueryShardContext context) {
-            return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name()));
-        }
-
-        @Override
-        public Query termQuery(Object value, QueryShardContext context) {
-            throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead");
-        }
-    }
-
-    protected Explicit<Boolean> coerce;
-    protected Explicit<Boolean> ignoreMalformed;
-    protected Explicit<Boolean> ignoreZValue;
-
-    protected BaseGeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
-                                     Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce,
-                                     Explicit<Boolean> ignoreZValue, Settings indexSettings,
-                                     MultiFields multiFields, CopyTo copyTo) {
-        super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, copyTo);
-        this.coerce = coerce;
-        this.ignoreMalformed = ignoreMalformed;
-        this.ignoreZValue = ignoreZValue;
-    }
-
-    @Override
-    protected void doMerge(Mapper mergeWith) {
-        super.doMerge(mergeWith);
-        BaseGeoShapeFieldMapper gsfm = (BaseGeoShapeFieldMapper)mergeWith;
-        if (gsfm.coerce.explicit()) {
-            this.coerce = gsfm.coerce;
-        }
-        if (gsfm.ignoreMalformed.explicit()) {
-            this.ignoreMalformed = gsfm.ignoreMalformed;
-        }
-        if (gsfm.ignoreZValue.explicit()) {
-            this.ignoreZValue = gsfm.ignoreZValue;
-        }
-    }
-
-    @Override
-    protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
-    }
-
-    @Override
-    protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
-        builder.field("type", contentType());
-        BaseGeoShapeFieldType ft = (BaseGeoShapeFieldType)fieldType();
-        if (includeDefaults || ft.orientation() != Defaults.ORIENTATION.value()) {
-            builder.field(Names.ORIENTATION.getPreferredName(), ft.orientation());
-        }
-        if (includeDefaults || coerce.explicit()) {
-            builder.field(Names.COERCE.getPreferredName(), coerce.value());
-        }
-        if (includeDefaults || ignoreMalformed.explicit()) {
-            builder.field(IGNORE_MALFORMED, ignoreMalformed.value());
-        }
-        if (includeDefaults || ignoreZValue.explicit()) {
-            builder.field(GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName(), ignoreZValue.value());
-        }
-    }
-
-    public Explicit<Boolean> coerce() {
-        return coerce;
-    }
-
-    public Explicit<Boolean> ignoreMalformed() {
-        return ignoreMalformed;
-    }
-
-    public Explicit<Boolean> ignoreZValue() {
-        return ignoreZValue;
-    }
-
-    public Orientation orientation() {
-        return ((BaseGeoShapeFieldType)fieldType).orientation();
-    }
-
-    @Override
-    protected String contentType() {
-        return CONTENT_TYPE;
-    }
-}

+ 530 - 80
server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java

@@ -18,24 +18,48 @@
  */
 package org.elasticsearch.index.mapper;
 
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.LatLonShape;
-import org.apache.lucene.geo.Line;
-import org.apache.lucene.geo.Polygon;
-import org.apache.lucene.geo.Rectangle;
+import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
+import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
+import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
+import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
+import org.apache.lucene.spatial.prefix.tree.PackedQuadPrefixTree;
+import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
+import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.Explicit;
-import org.elasticsearch.common.geo.GeoPoint;
+import org.elasticsearch.common.geo.GeoUtils;
+import org.elasticsearch.common.geo.SpatialStrategy;
+import org.elasticsearch.common.geo.XShapeCollection;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
+import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
 import org.elasticsearch.common.geo.parsers.ShapeParser;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.unit.DistanceUnit;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.support.XContentMapValues;
+import org.elasticsearch.index.query.QueryShardContext;
+import org.elasticsearch.index.query.QueryShardException;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Shape;
+import org.locationtech.spatial4j.shape.jts.JtsGeometry;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_MALFORMED;
 
 /**
- * FieldMapper for indexing {@link org.apache.lucene.document.LatLonShape}s.
+ * FieldMapper for indexing {@link org.locationtech.spatial4j.shape.Shape}s.
  * <p>
  * Currently Shapes can only be indexed and can only be queried using
  * {@link org.elasticsearch.index.query.GeoShapeQueryBuilder}, consequently
@@ -49,128 +73,554 @@ import java.util.Arrays;
  * [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
  * ]
  * }
- * <p>
- * or:
- * <p>
- * "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0))
  */
-public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
+public class GeoShapeFieldMapper extends FieldMapper {
+
+    public static final String CONTENT_TYPE = "geo_shape";
+
+    public static class Names {
+        public static final String TREE = "tree";
+        public static final String TREE_GEOHASH = "geohash";
+        public static final String TREE_QUADTREE = "quadtree";
+        public static final String TREE_LEVELS = "tree_levels";
+        public static final String TREE_PRESISION = "precision";
+        public static final String DISTANCE_ERROR_PCT = "distance_error_pct";
+        public static final String ORIENTATION = "orientation";
+        public static final String STRATEGY = "strategy";
+        public static final String STRATEGY_POINTS_ONLY = "points_only";
+        public static final String COERCE = "coerce";
+    }
+
+    public static class Defaults {
+        public static final String TREE = Names.TREE_GEOHASH;
+        public static final String STRATEGY = SpatialStrategy.RECURSIVE.getStrategyName();
+        public static final boolean POINTS_ONLY = false;
+        public static final int GEOHASH_LEVELS = GeoUtils.geoHashLevelsForPrecision("50m");
+        public static final int QUADTREE_LEVELS = GeoUtils.quadTreeLevelsForPrecision("50m");
+        public static final Orientation ORIENTATION = Orientation.RIGHT;
+        public static final double LEGACY_DISTANCE_ERROR_PCT = 0.025d;
+        public static final Explicit<Boolean> COERCE = new Explicit<>(false, false);
+        public static final Explicit<Boolean> IGNORE_MALFORMED = new Explicit<>(false, false);
+        public static final Explicit<Boolean> IGNORE_Z_VALUE = new Explicit<>(true, false);
+
+        public static final MappedFieldType FIELD_TYPE = new GeoShapeFieldType();
+
+        static {
+            // setting name here is a hack so freeze can be called...instead all these options should be
+            // moved to the default ctor for GeoShapeFieldType, and defaultFieldType() should be removed from mappers...
+            FIELD_TYPE.setName("DoesNotExist");
+            FIELD_TYPE.setIndexOptions(IndexOptions.DOCS);
+            FIELD_TYPE.setTokenized(false);
+            FIELD_TYPE.setStored(false);
+            FIELD_TYPE.setStoreTermVectors(false);
+            FIELD_TYPE.setOmitNorms(true);
+            FIELD_TYPE.freeze();
+        }
+    }
+
+    public static class Builder extends FieldMapper.Builder<Builder, GeoShapeFieldMapper> {
+
+        private Boolean coerce;
+        private Boolean ignoreMalformed;
+        private Boolean ignoreZValue;
 
-    public static class Builder extends BaseGeoShapeFieldMapper.Builder<BaseGeoShapeFieldMapper.Builder, GeoShapeFieldMapper> {
         public Builder(String name) {
-            super (name, new GeoShapeFieldType(), new GeoShapeFieldType());
+            super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
+        }
+
+        @Override
+        public GeoShapeFieldType fieldType() {
+            return (GeoShapeFieldType)fieldType;
         }
 
-        public Builder(String name, boolean coerce, boolean ignoreMalformed, ShapeBuilder.Orientation orientation,
-                       boolean ignoreZ) {
-            super(name, new GeoShapeFieldType(), new GeoShapeFieldType(), coerce, ignoreMalformed, orientation, ignoreZ);
+        public Builder coerce(boolean coerce) {
+            this.coerce = coerce;
+            return this;
+        }
+
+        @Override
+        protected boolean defaultDocValues(Version indexCreated) {
+            return false;
+        }
+
+        protected Explicit<Boolean> coerce(BuilderContext context) {
+            if (coerce != null) {
+                return new Explicit<>(coerce, true);
+            }
+            if (context.indexSettings() != null) {
+                return new Explicit<>(COERCE_SETTING.get(context.indexSettings()), false);
+            }
+            return Defaults.COERCE;
+        }
+
+        public Builder ignoreMalformed(boolean ignoreMalformed) {
+            this.ignoreMalformed = ignoreMalformed;
+            return this;
+        }
+
+        protected Explicit<Boolean> ignoreMalformed(BuilderContext context) {
+            if (ignoreMalformed != null) {
+                return new Explicit<>(ignoreMalformed, true);
+            }
+            if (context.indexSettings() != null) {
+                return new Explicit<>(IGNORE_MALFORMED_SETTING.get(context.indexSettings()), false);
+            }
+            return Defaults.IGNORE_MALFORMED;
+        }
+
+        protected Explicit<Boolean> ignoreZValue(BuilderContext context) {
+            if (ignoreZValue != null) {
+                return new Explicit<>(ignoreZValue, true);
+            }
+            return Defaults.IGNORE_Z_VALUE;
+        }
+
+        public Builder ignoreZValue(final boolean ignoreZValue) {
+            this.ignoreZValue = ignoreZValue;
+            return this;
         }
 
         @Override
         public GeoShapeFieldMapper build(BuilderContext context) {
+            GeoShapeFieldType geoShapeFieldType = (GeoShapeFieldType)fieldType;
+
+            if (geoShapeFieldType.treeLevels() == 0 && geoShapeFieldType.precisionInMeters() < 0) {
+                geoShapeFieldType.setDefaultDistanceErrorPct(Defaults.LEGACY_DISTANCE_ERROR_PCT);
+            }
             setupFieldType(context);
-            return new GeoShapeFieldMapper(name, fieldType, defaultFieldType, ignoreMalformed(context), coerce(context),
-                ignoreZValue(), context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);
+
+            return new GeoShapeFieldMapper(name, fieldType, ignoreMalformed(context), coerce(context), ignoreZValue(context),
+                    context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);
         }
     }
 
-    public static final class GeoShapeFieldType extends BaseGeoShapeFieldType {
-        public GeoShapeFieldType() {
-            super();
+    public static class TypeParser implements Mapper.TypeParser {
+
+        @Override
+        public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
+            Builder builder = new Builder(name);
+            Boolean pointsOnly = null;
+            for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry<String, Object> entry = iterator.next();
+                String fieldName = entry.getKey();
+                Object fieldNode = entry.getValue();
+                if (Names.TREE.equals(fieldName)) {
+                    builder.fieldType().setTree(fieldNode.toString());
+                    iterator.remove();
+                } else if (Names.TREE_LEVELS.equals(fieldName)) {
+                    builder.fieldType().setTreeLevels(Integer.parseInt(fieldNode.toString()));
+                    iterator.remove();
+                } else if (Names.TREE_PRESISION.equals(fieldName)) {
+                    builder.fieldType().setPrecisionInMeters(DistanceUnit.parse(fieldNode.toString(),
+                        DistanceUnit.DEFAULT, DistanceUnit.DEFAULT));
+                    iterator.remove();
+                } else if (Names.DISTANCE_ERROR_PCT.equals(fieldName)) {
+                    builder.fieldType().setDistanceErrorPct(Double.parseDouble(fieldNode.toString()));
+                    iterator.remove();
+                } else if (Names.ORIENTATION.equals(fieldName)) {
+                    builder.fieldType().setOrientation(ShapeBuilder.Orientation.fromString(fieldNode.toString()));
+                    iterator.remove();
+                } else if (Names.STRATEGY.equals(fieldName)) {
+                    builder.fieldType().setStrategyName(fieldNode.toString());
+                    iterator.remove();
+                } else if (IGNORE_MALFORMED.equals(fieldName)) {
+                    builder.ignoreMalformed(XContentMapValues.nodeBooleanValue(fieldNode, name + ".ignore_malformed"));
+                    iterator.remove();
+                } else if (Names.COERCE.equals(fieldName)) {
+                    builder.coerce(XContentMapValues.nodeBooleanValue(fieldNode, name + "." + Names.COERCE));
+                    iterator.remove();
+                } else if (GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName().equals(fieldName)) {
+                    builder.ignoreZValue(XContentMapValues.nodeBooleanValue(fieldNode,
+                        name + "." + GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName()));
+                    iterator.remove();
+                } else if (Names.STRATEGY_POINTS_ONLY.equals(fieldName)) {
+                    pointsOnly = XContentMapValues.nodeBooleanValue(fieldNode, name + "." + Names.STRATEGY_POINTS_ONLY);
+                    iterator.remove();
+                }
+            }
+            if (pointsOnly != null) {
+                if (builder.fieldType().strategyName.equals(SpatialStrategy.TERM.getStrategyName()) && pointsOnly == false) {
+                    throw new IllegalArgumentException("points_only cannot be set to false for term strategy");
+                } else {
+                    builder.fieldType().setPointsOnly(pointsOnly);
+                }
+            }
+            return builder;
         }
+    }
+
+    public static final class GeoShapeFieldType extends MappedFieldType {
+
+        private String tree = Defaults.TREE;
+        private String strategyName = Defaults.STRATEGY;
+        private boolean pointsOnly = Defaults.POINTS_ONLY;
+        private int treeLevels = 0;
+        private double precisionInMeters = -1;
+        private Double distanceErrorPct;
+        private double defaultDistanceErrorPct = 0.0;
+        private Orientation orientation = Defaults.ORIENTATION;
+
+        // these are built when the field type is frozen
+        private PrefixTreeStrategy defaultStrategy;
+        private RecursivePrefixTreeStrategy recursiveStrategy;
+        private TermQueryPrefixTreeStrategy termStrategy;
+
+        public GeoShapeFieldType() {}
 
         protected GeoShapeFieldType(GeoShapeFieldType ref) {
             super(ref);
+            this.tree = ref.tree;
+            this.strategyName = ref.strategyName;
+            this.pointsOnly = ref.pointsOnly;
+            this.treeLevels = ref.treeLevels;
+            this.precisionInMeters = ref.precisionInMeters;
+            this.distanceErrorPct = ref.distanceErrorPct;
+            this.defaultDistanceErrorPct = ref.defaultDistanceErrorPct;
+            this.orientation = ref.orientation;
         }
 
         @Override
         public GeoShapeFieldType clone() {
             return new GeoShapeFieldType(this);
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!super.equals(o)) return false;
+            GeoShapeFieldType that = (GeoShapeFieldType) o;
+            return treeLevels == that.treeLevels &&
+                precisionInMeters == that.precisionInMeters &&
+                defaultDistanceErrorPct == that.defaultDistanceErrorPct &&
+                Objects.equals(tree, that.tree) &&
+                Objects.equals(strategyName, that.strategyName) &&
+                pointsOnly == that.pointsOnly &&
+                Objects.equals(distanceErrorPct, that.distanceErrorPct) &&
+                orientation == that.orientation;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(), tree, strategyName, pointsOnly, treeLevels, precisionInMeters, distanceErrorPct,
+                    defaultDistanceErrorPct, orientation);
+        }
+
+        @Override
+        public String typeName() {
+            return CONTENT_TYPE;
+        }
+
+        @Override
+        public void freeze() {
+            super.freeze();
+            // This is a bit hackish: we need to setup the spatial tree and strategies once the field name is set, which
+            // must be by the time freeze is called.
+            SpatialPrefixTree prefixTree;
+            if ("geohash".equals(tree)) {
+                prefixTree = new GeohashPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
+                    getLevels(treeLevels, precisionInMeters, Defaults.GEOHASH_LEVELS, true));
+            } else if ("legacyquadtree".equals(tree)) {
+                prefixTree = new QuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
+                    getLevels(treeLevels, precisionInMeters, Defaults.QUADTREE_LEVELS, false));
+            } else if ("quadtree".equals(tree)) {
+                prefixTree = new PackedQuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
+                    getLevels(treeLevels, precisionInMeters, Defaults.QUADTREE_LEVELS, false));
+            } else {
+                throw new IllegalArgumentException("Unknown prefix tree type [" + tree + "]");
+            }
+
+            recursiveStrategy = new RecursivePrefixTreeStrategy(prefixTree, name());
+            recursiveStrategy.setDistErrPct(distanceErrorPct());
+            recursiveStrategy.setPruneLeafyBranches(false);
+            termStrategy = new TermQueryPrefixTreeStrategy(prefixTree, name());
+            termStrategy.setDistErrPct(distanceErrorPct());
+            defaultStrategy = resolveStrategy(strategyName);
+            defaultStrategy.setPointsOnly(pointsOnly);
+        }
+
+        @Override
+        public void checkCompatibility(MappedFieldType fieldType, List<String> conflicts) {
+            super.checkCompatibility(fieldType, conflicts);
+            GeoShapeFieldType other = (GeoShapeFieldType)fieldType;
+            // prevent user from changing strategies
+            if (strategyName().equals(other.strategyName()) == false) {
+                conflicts.add("mapper [" + name() + "] has different [strategy]");
+            }
+
+            // prevent user from changing trees (changes encoding)
+            if (tree().equals(other.tree()) == false) {
+                conflicts.add("mapper [" + name() + "] has different [tree]");
+            }
+
+            if ((pointsOnly() != other.pointsOnly())) {
+                conflicts.add("mapper [" + name() + "] has different points_only");
+            }
+
+            // TODO we should allow this, but at the moment levels is used to build bookkeeping variables
+            // in lucene's SpatialPrefixTree implementations, need a patch to correct that first
+            if (treeLevels() != other.treeLevels()) {
+                conflicts.add("mapper [" + name() + "] has different [tree_levels]");
+            }
+            if (precisionInMeters() != other.precisionInMeters()) {
+                conflicts.add("mapper [" + name() + "] has different [precision]");
+            }
+        }
+
+        private static int getLevels(int treeLevels, double precisionInMeters, int defaultLevels, boolean geoHash) {
+            if (treeLevels > 0 || precisionInMeters >= 0) {
+                return Math.max(treeLevels, precisionInMeters >= 0 ? (geoHash ? GeoUtils.geoHashLevelsForPrecision(precisionInMeters)
+                    : GeoUtils.quadTreeLevelsForPrecision(precisionInMeters)) : 0);
+            }
+            return defaultLevels;
+        }
+
+        public String tree() {
+            return tree;
+        }
+
+        public void setTree(String tree) {
+            checkIfFrozen();
+            this.tree = tree;
+        }
+
+        public String strategyName() {
+            return strategyName;
+        }
+
+        public void setStrategyName(String strategyName) {
+            checkIfFrozen();
+            this.strategyName = strategyName;
+            if (this.strategyName.equals(SpatialStrategy.TERM.getStrategyName())) {
+                this.pointsOnly = true;
+            }
+        }
+
+        public boolean pointsOnly() {
+            return pointsOnly;
+        }
+
+        public void setPointsOnly(boolean pointsOnly) {
+            checkIfFrozen();
+            this.pointsOnly = pointsOnly;
+        }
+        public int treeLevels() {
+            return treeLevels;
+        }
+
+        public void setTreeLevels(int treeLevels) {
+            checkIfFrozen();
+            this.treeLevels = treeLevels;
+        }
+
+        public double precisionInMeters() {
+            return precisionInMeters;
+        }
+
+        public void setPrecisionInMeters(double precisionInMeters) {
+            checkIfFrozen();
+            this.precisionInMeters = precisionInMeters;
+        }
+
+        public double distanceErrorPct() {
+            return distanceErrorPct == null ? defaultDistanceErrorPct : distanceErrorPct;
+        }
+
+        public void setDistanceErrorPct(double distanceErrorPct) {
+            checkIfFrozen();
+            this.distanceErrorPct = distanceErrorPct;
+        }
+
+        public void setDefaultDistanceErrorPct(double defaultDistanceErrorPct) {
+            checkIfFrozen();
+            this.defaultDistanceErrorPct = defaultDistanceErrorPct;
+        }
+
+        public Orientation orientation() { return this.orientation; }
+
+        public void setOrientation(Orientation orientation) {
+            checkIfFrozen();
+            this.orientation = orientation;
+        }
+
+        public PrefixTreeStrategy defaultStrategy() {
+            return this.defaultStrategy;
+        }
+
+        public PrefixTreeStrategy resolveStrategy(SpatialStrategy strategy) {
+            return resolveStrategy(strategy.getStrategyName());
+        }
+
+        public PrefixTreeStrategy resolveStrategy(String strategyName) {
+            if (SpatialStrategy.RECURSIVE.getStrategyName().equals(strategyName)) {
+                return recursiveStrategy;
+            }
+            if (SpatialStrategy.TERM.getStrategyName().equals(strategyName)) {
+                return termStrategy;
+            }
+            throw new IllegalArgumentException("Unknown prefix tree strategy [" + strategyName + "]");
+        }
+
+        @Override
+        public Query existsQuery(QueryShardContext context) {
+            return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name()));
+        }
+
+        @Override
+        public Query termQuery(Object value, QueryShardContext context) {
+            throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead");
+        }
     }
 
-    public GeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
-                               Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce,
-                               Explicit<Boolean> ignoreZValue, Settings indexSettings,
+    protected Explicit<Boolean> coerce;
+    protected Explicit<Boolean> ignoreMalformed;
+    protected Explicit<Boolean> ignoreZValue;
+
+    public GeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, Explicit<Boolean> ignoreMalformed,
+                               Explicit<Boolean> coerce, Explicit<Boolean> ignoreZValue, Settings indexSettings,
                                MultiFields multiFields, CopyTo copyTo) {
-        super(simpleName, fieldType, defaultFieldType, ignoreMalformed, coerce, ignoreZValue, indexSettings,
-            multiFields, copyTo);
+        super(simpleName, fieldType, Defaults.FIELD_TYPE, indexSettings, multiFields, copyTo);
+        this.coerce = coerce;
+        this.ignoreMalformed = ignoreMalformed;
+        this.ignoreZValue = ignoreZValue;
     }
 
     @Override
     public GeoShapeFieldType fieldType() {
         return (GeoShapeFieldType) super.fieldType();
     }
-
-    /** parsing logic for {@link LatLonShape} indexing */
     @Override
     public void parse(ParseContext context) throws IOException {
         try {
-            Object shape = context.parseExternalValue(Object.class);
+            Shape shape = context.parseExternalValue(Shape.class);
             if (shape == null) {
                 ShapeBuilder shapeBuilder = ShapeParser.parse(context.parser(), this);
                 if (shapeBuilder == null) {
                     return;
                 }
-                shape = shapeBuilder.buildLucene();
+                shape = shapeBuilder.buildS4J();
+            }
+            if (fieldType().pointsOnly() == true) {
+                // index configured for pointsOnly
+                if (shape instanceof XShapeCollection && XShapeCollection.class.cast(shape).pointsOnly()) {
+                    // MULTIPOINT data: index each point separately
+                    List<Shape> shapes = ((XShapeCollection) shape).getShapes();
+                    for (Shape s : shapes) {
+                        indexShape(context, s);
+                    }
+                    return;
+                } else if (shape instanceof Point == false) {
+                    throw new MapperParsingException("[{" + fieldType().name() + "}] is configured for points only but a "
+                        + ((shape instanceof JtsGeometry) ? ((JtsGeometry)shape).getGeom().getGeometryType() : shape.getClass())
+                        + " was found");
+                }
             }
             indexShape(context, shape);
         } catch (Exception e) {
             if (ignoreMalformed.value() == false) {
                 throw new MapperParsingException("failed to parse field [{}] of type [{}]", e, fieldType().name(),
-                    fieldType().typeName());
-            }
-            context.addIgnoredField(fieldType().name());
-        }
-    }
-
-    private void indexShape(ParseContext context, Object luceneShape) {
-        if (luceneShape instanceof GeoPoint) {
-            GeoPoint pt = (GeoPoint) luceneShape;
-            indexFields(context, LatLonShape.createIndexableFields(name(), pt.lat(), pt.lon()));
-        } else if (luceneShape instanceof double[]) {
-            double[] pt = (double[]) luceneShape;
-            indexFields(context, LatLonShape.createIndexableFields(name(), pt[1], pt[0]));
-        } else if (luceneShape instanceof Line) {
-            indexFields(context, LatLonShape.createIndexableFields(name(), (Line)luceneShape));
-        } else if (luceneShape instanceof Polygon) {
-            indexFields(context, LatLonShape.createIndexableFields(name(), (Polygon) luceneShape));
-        } else if (luceneShape instanceof double[][]) {
-            double[][] pts = (double[][])luceneShape;
-            for (int i = 0; i < pts.length; ++i) {
-                indexFields(context, LatLonShape.createIndexableFields(name(), pts[i][1], pts[i][0]));
-            }
-        } else if (luceneShape instanceof Line[]) {
-            Line[] lines = (Line[]) luceneShape;
-            for (int i = 0; i < lines.length; ++i) {
-                indexFields(context, LatLonShape.createIndexableFields(name(), lines[i]));
-            }
-        } else if (luceneShape instanceof Polygon[]) {
-            Polygon[] polys = (Polygon[]) luceneShape;
-            for (int i = 0; i < polys.length; ++i) {
-                indexFields(context, LatLonShape.createIndexableFields(name(), polys[i]));
-            }
-        } else if (luceneShape instanceof Rectangle) {
-            // index rectangle as a polygon
-            Rectangle r = (Rectangle) luceneShape;
-            Polygon p = new Polygon(new double[]{r.minLat, r.minLat, r.maxLat, r.maxLat, r.minLat},
-                new double[]{r.minLon, r.maxLon, r.maxLon, r.minLon, r.minLon});
-            indexFields(context, LatLonShape.createIndexableFields(name(), p));
-        } else if (luceneShape instanceof Object[]) {
-            // recurse to index geometry collection
-            for (Object o : (Object[])luceneShape) {
-                indexShape(context, o);
+                        fieldType().typeName());
             }
-        } else {
-            throw new IllegalArgumentException("invalid shape type found [" + luceneShape.getClass() + "] while indexing shape");
+            context.addIgnoredField(fieldType.name());
+        }
+    }
+
+    private void indexShape(ParseContext context, Shape shape) {
+        List<IndexableField> fields = new ArrayList<>(Arrays.asList(fieldType().defaultStrategy().createIndexableFields(shape)));
+        createFieldNamesField(context, fields);
+        for (IndexableField field : fields) {
+            context.doc().add(field);
+        }
+    }
+
+    @Override
+    protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
+    }
+
+    @Override
+    protected void doMerge(Mapper mergeWith) {
+        super.doMerge(mergeWith);
+
+        GeoShapeFieldMapper gsfm = (GeoShapeFieldMapper)mergeWith;
+        if (gsfm.coerce.explicit()) {
+            this.coerce = gsfm.coerce;
+        }
+        if (gsfm.ignoreMalformed.explicit()) {
+            this.ignoreMalformed = gsfm.ignoreMalformed;
+        }
+        if (gsfm.ignoreZValue.explicit()) {
+            this.ignoreZValue = gsfm.ignoreZValue;
         }
     }
 
-    private void indexFields(ParseContext context, Field[] fields) {
-        ArrayList<IndexableField> flist = new ArrayList<>(Arrays.asList(fields));
-        createFieldNamesField(context, flist);
-        for (IndexableField f : flist) {
-            context.doc().add(f);
+    @Override
+    protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
+        builder.field("type", contentType());
+
+        if (includeDefaults || fieldType().tree().equals(Defaults.TREE) == false) {
+            builder.field(Names.TREE, fieldType().tree());
+        }
+
+        if (fieldType().treeLevels() != 0) {
+            builder.field(Names.TREE_LEVELS, fieldType().treeLevels());
+        } else if(includeDefaults && fieldType().precisionInMeters() == -1) { // defaults only make sense if precision is not specified
+            if ("geohash".equals(fieldType().tree())) {
+                builder.field(Names.TREE_LEVELS, Defaults.GEOHASH_LEVELS);
+            } else if ("legacyquadtree".equals(fieldType().tree())) {
+                builder.field(Names.TREE_LEVELS, Defaults.QUADTREE_LEVELS);
+            } else if ("quadtree".equals(fieldType().tree())) {
+                builder.field(Names.TREE_LEVELS, Defaults.QUADTREE_LEVELS);
+            } else {
+                throw new IllegalArgumentException("Unknown prefix tree type [" + fieldType().tree() + "]");
+            }
         }
+        if (fieldType().precisionInMeters() != -1) {
+            builder.field(Names.TREE_PRESISION, DistanceUnit.METERS.toString(fieldType().precisionInMeters()));
+        } else if (includeDefaults && fieldType().treeLevels() == 0) { // defaults only make sense if tree levels are not specified
+            builder.field(Names.TREE_PRESISION, DistanceUnit.METERS.toString(50));
+        }
+        if (includeDefaults || fieldType().strategyName().equals(Defaults.STRATEGY) == false) {
+            builder.field(Names.STRATEGY, fieldType().strategyName());
+        }
+        if (includeDefaults || fieldType().distanceErrorPct() != fieldType().defaultDistanceErrorPct) {
+            builder.field(Names.DISTANCE_ERROR_PCT, fieldType().distanceErrorPct());
+        }
+        if (includeDefaults || fieldType().orientation() != Defaults.ORIENTATION) {
+            builder.field(Names.ORIENTATION, fieldType().orientation());
+        }
+        if (fieldType().strategyName().equals(SpatialStrategy.TERM.getStrategyName())) {
+            // For TERMs strategy the defaults for points only change to true
+            if (includeDefaults || fieldType().pointsOnly() != true) {
+                builder.field(Names.STRATEGY_POINTS_ONLY, fieldType().pointsOnly());
+            }
+        } else {
+            if (includeDefaults || fieldType().pointsOnly() != GeoShapeFieldMapper.Defaults.POINTS_ONLY) {
+                builder.field(Names.STRATEGY_POINTS_ONLY, fieldType().pointsOnly());
+            }
+        }
+        if (includeDefaults || coerce.explicit()) {
+            builder.field(Names.COERCE, coerce.value());
+        }
+        if (includeDefaults || ignoreMalformed.explicit()) {
+            builder.field(IGNORE_MALFORMED, ignoreMalformed.value());
+        }
+        if (includeDefaults || ignoreZValue.explicit()) {
+            builder.field(GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName(), ignoreZValue.value());
+        }
+    }
+
+    public Explicit<Boolean> coerce() {
+        return coerce;
+    }
+
+    public Explicit<Boolean> ignoreMalformed() {
+        return ignoreMalformed;
+    }
+
+    public Explicit<Boolean> ignoreZValue() {
+        return ignoreZValue;
+    }
+
+    @Override
+    protected String contentType() {
+        return CONTENT_TYPE;
     }
 }

+ 0 - 596
server/src/main/java/org/elasticsearch/index/mapper/LegacyGeoShapeFieldMapper.java

@@ -1,596 +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.mapper;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.lucene.index.IndexOptions;
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
-import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
-import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
-import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
-import org.apache.lucene.spatial.prefix.tree.PackedQuadPrefixTree;
-import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
-import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
-import org.elasticsearch.ElasticsearchParseException;
-import org.elasticsearch.common.Explicit;
-import org.elasticsearch.common.ParseField;
-import org.elasticsearch.common.geo.GeoUtils;
-import org.elasticsearch.common.geo.ShapesAvailability;
-import org.elasticsearch.common.geo.SpatialStrategy;
-import org.elasticsearch.common.geo.XShapeCollection;
-import org.elasticsearch.common.geo.builders.ShapeBuilder;
-import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
-import org.elasticsearch.common.geo.parsers.ShapeParser;
-import org.elasticsearch.common.logging.DeprecationLogger;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.unit.DistanceUnit;
-import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.support.XContentMapValues;
-import org.locationtech.spatial4j.shape.Point;
-import org.locationtech.spatial4j.shape.Shape;
-import org.locationtech.spatial4j.shape.jts.JtsGeometry;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * FieldMapper for indexing {@link org.locationtech.spatial4j.shape.Shape}s.
- * <p>
- * Currently Shapes can only be indexed and can only be queried using
- * {@link org.elasticsearch.index.query.GeoShapeQueryBuilder}, consequently
- * a lot of behavior in this Mapper is disabled.
- * <p>
- * Format supported:
- * <p>
- * "field" : {
- * "type" : "polygon",
- * "coordinates" : [
- * [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
- * ]
- * }
- * <p>
- * or:
- * <p>
- * "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0))
- *
- * @deprecated use {@link GeoShapeFieldMapper}
- */
-@Deprecated
-public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
-
-    public static final String CONTENT_TYPE = "geo_shape";
-
-    @Deprecated
-    public static class DeprecatedParameters {
-        public static class Names {
-            public static final ParseField STRATEGY = new ParseField("strategy");
-            public static final ParseField TREE = new ParseField("tree");
-            public static final ParseField TREE_LEVELS = new ParseField("tree_levels");
-            public static final ParseField PRECISION = new ParseField("precision");
-            public static final ParseField DISTANCE_ERROR_PCT = new ParseField("distance_error_pct");
-            public static final ParseField POINTS_ONLY = new ParseField("points_only");
-        }
-
-        public static class PrefixTrees {
-            public static final String LEGACY_QUADTREE = "legacyquadtree";
-            public static final String QUADTREE = "quadtree";
-            public static final String GEOHASH = "geohash";
-        }
-
-        public static class Defaults {
-            public static final SpatialStrategy STRATEGY = SpatialStrategy.RECURSIVE;
-            public static final String TREE = "quadtree";
-            public static final String PRECISION = "50m";
-            public static final int QUADTREE_LEVELS = GeoUtils.quadTreeLevelsForPrecision(PRECISION);
-            public static final int GEOHASH_TREE_LEVELS = GeoUtils.geoHashLevelsForPrecision(PRECISION);
-            public static final boolean POINTS_ONLY = false;
-            public static final double DISTANCE_ERROR_PCT = 0.025d;
-        }
-
-        public SpatialStrategy strategy = null;
-        public String tree = null;
-        public int treeLevels = Integer.MIN_VALUE;
-        public String precision = null;
-        public Boolean pointsOnly = null;
-        public double distanceErrorPct = Double.NaN;
-
-        public void setSpatialStrategy(SpatialStrategy strategy) {
-            this.strategy = strategy;
-        }
-
-        public void setTree(String prefixTree) {
-            this.tree = prefixTree;
-        }
-
-        public void setTreeLevels(int treeLevels) {
-            this.treeLevels = treeLevels;
-        }
-
-        public void setPrecision(String precision) {
-            this.precision = precision;
-        }
-
-        public void setPointsOnly(boolean pointsOnly) {
-            if (this.strategy == SpatialStrategy.TERM && pointsOnly == false) {
-                throw new ElasticsearchParseException("points_only cannot be set to false for term strategy");
-            }
-            this.pointsOnly = pointsOnly;
-        }
-
-        public void setDistanceErrorPct(double distanceErrorPct) {
-            this.distanceErrorPct = distanceErrorPct;
-        }
-
-        protected void setup() {
-            if (strategy == null) {
-                strategy = Defaults.STRATEGY;
-            }
-            if (tree == null) {
-                tree = Defaults.TREE;
-            }
-            if (Double.isNaN(distanceErrorPct)) {
-                if (precision != null || treeLevels != Integer.MIN_VALUE) {
-                    distanceErrorPct = 0d;
-                } else {
-                    distanceErrorPct = Defaults.DISTANCE_ERROR_PCT;
-                }
-            }
-            if (treeLevels == Integer.MIN_VALUE && precision == null) {
-                // set default precision if treeLevels is not explicitly set
-                precision = Defaults.PRECISION;
-            }
-            if (treeLevels == Integer.MIN_VALUE) {
-                if (precision.equals(Defaults.PRECISION)) {
-                    treeLevels = tree.equals(Defaults.TREE)
-                        ? Defaults.QUADTREE_LEVELS
-                        : Defaults.GEOHASH_TREE_LEVELS;
-                } else {
-                    treeLevels = tree == Defaults.TREE
-                        ? GeoUtils.quadTreeLevelsForPrecision(precision)
-                        : GeoUtils.geoHashLevelsForPrecision(precision);
-                }
-            }
-            if (pointsOnly == null) {
-                if (strategy == SpatialStrategy.TERM) {
-                    pointsOnly = true;
-                } else {
-                    pointsOnly = Defaults.POINTS_ONLY;
-                }
-            }
-        }
-
-        public static boolean parse(String name, String fieldName, Object fieldNode, DeprecatedParameters deprecatedParameters) {
-            if (Names.STRATEGY.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                checkPrefixTreeSupport(fieldName);
-                deprecatedParameters.setSpatialStrategy(SpatialStrategy.fromString(fieldNode.toString()));
-            } else if (Names.TREE.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                checkPrefixTreeSupport(fieldName);
-                deprecatedParameters.setTree(fieldNode.toString());
-            } else if (Names.TREE_LEVELS.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                checkPrefixTreeSupport(fieldName);
-                deprecatedParameters.setTreeLevels(Integer.parseInt(fieldNode.toString()));
-            } else if (Names.PRECISION.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                checkPrefixTreeSupport(fieldName);
-                deprecatedParameters.setPrecision(fieldNode.toString());
-            } else if (Names.DISTANCE_ERROR_PCT.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                checkPrefixTreeSupport(fieldName);
-                deprecatedParameters.setDistanceErrorPct(Double.parseDouble(fieldNode.toString()));
-            } else if (Names.POINTS_ONLY.match(fieldName, LoggingDeprecationHandler.INSTANCE)) {
-                checkPrefixTreeSupport(fieldName);
-                deprecatedParameters.setPointsOnly(
-                    XContentMapValues.nodeBooleanValue(fieldNode, name + "." + DeprecatedParameters.Names.POINTS_ONLY));
-            } else {
-                return false;
-            }
-            return true;
-        }
-
-        private static void checkPrefixTreeSupport(String fieldName) {
-            if (ShapesAvailability.JTS_AVAILABLE == false || ShapesAvailability.SPATIAL4J_AVAILABLE == false) {
-                throw new ElasticsearchParseException("Field parameter [{}] is not supported for [{}] field type",
-                    fieldName, CONTENT_TYPE);
-            }
-            DEPRECATION_LOGGER.deprecated("Field parameter [{}] is deprecated and will be removed in a future version.",
-                fieldName);
-        }
-    }
-
-    private static final Logger logger = LogManager.getLogger(LegacyGeoShapeFieldMapper.class);
-    private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(logger);
-
-    public static class Builder extends BaseGeoShapeFieldMapper.Builder<BaseGeoShapeFieldMapper.Builder, LegacyGeoShapeFieldMapper> {
-
-        DeprecatedParameters deprecatedParameters;
-
-        public Builder(String name) {
-            super(name, new GeoShapeFieldType(), new GeoShapeFieldType());
-            this.deprecatedParameters = new DeprecatedParameters();
-            this.deprecatedParameters.setup();
-        }
-
-        public Builder(String name, boolean coerce, boolean ignoreMalformed, Orientation orientation,
-                       boolean ignoreZ, DeprecatedParameters deprecatedParameters) {
-            super(name, new GeoShapeFieldType(), new GeoShapeFieldType(), coerce, ignoreMalformed, orientation, ignoreZ);
-            this.deprecatedParameters = deprecatedParameters;
-            this.deprecatedParameters.setup();
-        }
-
-        @Override
-        public GeoShapeFieldType fieldType() {
-            return (GeoShapeFieldType)fieldType;
-        }
-
-        private void setupFieldTypeDeprecatedParameters() {
-            GeoShapeFieldType ft = fieldType();
-            ft.setStrategy(deprecatedParameters.strategy);
-            ft.setTree(deprecatedParameters.tree);
-            ft.setTreeLevels(deprecatedParameters.treeLevels);
-            if (deprecatedParameters.precision != null) {
-                // precision is only set iff: a. treeLevel is not explicitly set, b. its explicitly set
-                ft.setPrecisionInMeters(DistanceUnit.parse(deprecatedParameters.precision,
-                    DistanceUnit.DEFAULT, DistanceUnit.DEFAULT));
-            }
-            ft.setDistanceErrorPct(deprecatedParameters.distanceErrorPct);
-            ft.setPointsOnly(deprecatedParameters.pointsOnly);
-        }
-
-        private void setupPrefixTrees() {
-            GeoShapeFieldType ft = fieldType();
-            SpatialPrefixTree prefixTree;
-            if (ft.tree().equals(DeprecatedParameters.PrefixTrees.GEOHASH)) {
-                prefixTree = new GeohashPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
-                    getLevels(ft.treeLevels(), ft.precisionInMeters(), DeprecatedParameters.Defaults.GEOHASH_TREE_LEVELS, true));
-            } else if (ft.tree().equals(DeprecatedParameters.PrefixTrees.LEGACY_QUADTREE)) {
-                prefixTree = new QuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
-                    getLevels(ft.treeLevels(), ft.precisionInMeters(), DeprecatedParameters.Defaults.QUADTREE_LEVELS, false));
-            } else if (ft.tree().equals(DeprecatedParameters.PrefixTrees.QUADTREE)) {
-                prefixTree = new PackedQuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
-                    getLevels(ft.treeLevels(), ft.precisionInMeters(), DeprecatedParameters.Defaults.QUADTREE_LEVELS, false));
-            } else {
-                throw new IllegalArgumentException("Unknown prefix tree type [" + ft.tree() + "]");
-            }
-
-            // setup prefix trees regardless of strategy (this is used for the QueryBuilder)
-            // recursive:
-            RecursivePrefixTreeStrategy rpts = new RecursivePrefixTreeStrategy(prefixTree, ft.name());
-            rpts.setDistErrPct(ft.distanceErrorPct());
-            rpts.setPruneLeafyBranches(false);
-            ft.recursiveStrategy = rpts;
-
-            // term:
-            TermQueryPrefixTreeStrategy termStrategy = new TermQueryPrefixTreeStrategy(prefixTree, ft.name());
-            termStrategy.setDistErrPct(ft.distanceErrorPct());
-            ft.termStrategy = termStrategy;
-
-            // set default (based on strategy):
-            ft.defaultPrefixTreeStrategy = ft.resolvePrefixTreeStrategy(ft.strategy());
-            ft.defaultPrefixTreeStrategy.setPointsOnly(ft.pointsOnly());
-        }
-
-        @Override
-        protected void setupFieldType(BuilderContext context) {
-            super.setupFieldType(context);
-
-            // field mapper handles this at build time
-            // but prefix tree strategies require a name, so throw a similar exception
-            if (fieldType().name().isEmpty()) {
-                throw new IllegalArgumentException("name cannot be empty string");
-            }
-
-            // setup the deprecated parameters and the prefix tree configuration
-            setupFieldTypeDeprecatedParameters();
-            setupPrefixTrees();
-        }
-
-        private static int getLevels(int treeLevels, double precisionInMeters, int defaultLevels, boolean geoHash) {
-            if (treeLevels > 0 || precisionInMeters >= 0) {
-                return Math.max(treeLevels, precisionInMeters >= 0 ? (geoHash ? GeoUtils.geoHashLevelsForPrecision(precisionInMeters)
-                    : GeoUtils.quadTreeLevelsForPrecision(precisionInMeters)) : 0);
-            }
-            return defaultLevels;
-        }
-
-        @Override
-        public LegacyGeoShapeFieldMapper build(BuilderContext context) {
-            setupFieldType(context);
-
-            return new LegacyGeoShapeFieldMapper(name, fieldType, defaultFieldType, ignoreMalformed(context),
-                coerce(context), orientation(), ignoreZValue(), context.indexSettings(),
-                multiFieldsBuilder.build(this, context), copyTo);
-        }
-    }
-
-    public static final class GeoShapeFieldType extends BaseGeoShapeFieldType {
-
-        private String tree = DeprecatedParameters.Defaults.TREE;
-        private SpatialStrategy strategy = DeprecatedParameters.Defaults.STRATEGY;
-        private boolean pointsOnly = DeprecatedParameters.Defaults.POINTS_ONLY;
-        private int treeLevels = 0;
-        private double precisionInMeters = -1;
-        private Double distanceErrorPct;
-        private double defaultDistanceErrorPct = 0.0;
-
-        // these are built when the field type is frozen
-        private PrefixTreeStrategy defaultPrefixTreeStrategy;
-        private RecursivePrefixTreeStrategy recursiveStrategy;
-        private TermQueryPrefixTreeStrategy termStrategy;
-
-        public GeoShapeFieldType() {
-            setIndexOptions(IndexOptions.DOCS);
-            setTokenized(false);
-            setStored(false);
-            setStoreTermVectors(false);
-            setOmitNorms(true);
-        }
-
-        protected GeoShapeFieldType(GeoShapeFieldType ref) {
-            super(ref);
-            this.tree = ref.tree;
-            this.strategy = ref.strategy;
-            this.pointsOnly = ref.pointsOnly;
-            this.treeLevels = ref.treeLevels;
-            this.precisionInMeters = ref.precisionInMeters;
-            this.distanceErrorPct = ref.distanceErrorPct;
-            this.defaultDistanceErrorPct = ref.defaultDistanceErrorPct;
-        }
-
-        @Override
-        public GeoShapeFieldType clone() {
-            return new GeoShapeFieldType(this);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (!super.equals(o)) return false;
-            GeoShapeFieldType that = (GeoShapeFieldType) o;
-            return treeLevels == that.treeLevels &&
-                precisionInMeters == that.precisionInMeters &&
-                defaultDistanceErrorPct == that.defaultDistanceErrorPct &&
-                Objects.equals(tree, that.tree) &&
-                Objects.equals(strategy, that.strategy) &&
-                pointsOnly == that.pointsOnly &&
-                Objects.equals(distanceErrorPct, that.distanceErrorPct);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(super.hashCode(), tree, strategy, pointsOnly, treeLevels, precisionInMeters, distanceErrorPct,
-                    defaultDistanceErrorPct);
-        }
-
-        @Override
-        public void checkCompatibility(MappedFieldType fieldType, List<String> conflicts) {
-            super.checkCompatibility(fieldType, conflicts);
-            GeoShapeFieldType other = (GeoShapeFieldType)fieldType;
-            // prevent user from changing strategies
-            if (strategy() != other.strategy()) {
-                conflicts.add("mapper [" + name() + "] has different [strategy]");
-            }
-
-            // prevent user from changing trees (changes encoding)
-            if (tree().equals(other.tree()) == false) {
-                conflicts.add("mapper [" + name() + "] has different [tree]");
-            }
-
-            if ((pointsOnly() != other.pointsOnly())) {
-                conflicts.add("mapper [" + name() + "] has different points_only");
-            }
-
-            // TODO we should allow this, but at the moment levels is used to build bookkeeping variables
-            // in lucene's SpatialPrefixTree implementations, need a patch to correct that first
-            if (treeLevels() != other.treeLevels()) {
-                conflicts.add("mapper [" + name() + "] has different [tree_levels]");
-            }
-            if (precisionInMeters() != other.precisionInMeters()) {
-                conflicts.add("mapper [" + name() + "] has different [precision]");
-            }
-        }
-
-        public String tree() {
-            return tree;
-        }
-
-        public void setTree(String tree) {
-            checkIfFrozen();
-            this.tree = tree;
-        }
-
-        public SpatialStrategy strategy() {
-            return strategy;
-        }
-
-        public void setStrategy(SpatialStrategy strategy) {
-            checkIfFrozen();
-            this.strategy = strategy;
-            if (this.strategy.equals(SpatialStrategy.TERM)) {
-                this.pointsOnly = true;
-            }
-        }
-
-        public boolean pointsOnly() {
-            return pointsOnly;
-        }
-
-        public void setPointsOnly(boolean pointsOnly) {
-            checkIfFrozen();
-            this.pointsOnly = pointsOnly;
-        }
-        public int treeLevels() {
-            return treeLevels;
-        }
-
-        public void setTreeLevels(int treeLevels) {
-            checkIfFrozen();
-            this.treeLevels = treeLevels;
-        }
-
-        public double precisionInMeters() {
-            return precisionInMeters;
-        }
-
-        public void setPrecisionInMeters(double precisionInMeters) {
-            checkIfFrozen();
-            this.precisionInMeters = precisionInMeters;
-        }
-
-        public double distanceErrorPct() {
-            return distanceErrorPct == null ? defaultDistanceErrorPct : distanceErrorPct;
-        }
-
-        public void setDistanceErrorPct(double distanceErrorPct) {
-            checkIfFrozen();
-            this.distanceErrorPct = distanceErrorPct;
-        }
-
-        public void setDefaultDistanceErrorPct(double defaultDistanceErrorPct) {
-            checkIfFrozen();
-            this.defaultDistanceErrorPct = defaultDistanceErrorPct;
-        }
-
-        public PrefixTreeStrategy defaultPrefixTreeStrategy() {
-            return this.defaultPrefixTreeStrategy;
-        }
-
-        public PrefixTreeStrategy resolvePrefixTreeStrategy(SpatialStrategy strategy) {
-            return resolvePrefixTreeStrategy(strategy.getStrategyName());
-        }
-
-        public PrefixTreeStrategy resolvePrefixTreeStrategy(String strategyName) {
-            if (SpatialStrategy.RECURSIVE.getStrategyName().equals(strategyName)) {
-                return recursiveStrategy;
-            }
-            if (SpatialStrategy.TERM.getStrategyName().equals(strategyName)) {
-                return termStrategy;
-            }
-            throw new IllegalArgumentException("Unknown prefix tree strategy [" + strategyName + "]");
-        }
-    }
-
-    public LegacyGeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
-                               Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce, Explicit<Orientation> orientation,
-                               Explicit<Boolean> ignoreZValue, Settings indexSettings,
-                               MultiFields multiFields, CopyTo copyTo) {
-        super(simpleName, fieldType, defaultFieldType, ignoreMalformed, coerce, ignoreZValue, indexSettings,
-            multiFields, copyTo);
-    }
-
-    @Override
-    public GeoShapeFieldType fieldType() {
-        return (GeoShapeFieldType) super.fieldType();
-    }
-
-    @Override
-    public void parse(ParseContext context) throws IOException {
-        try {
-            Shape shape = context.parseExternalValue(Shape.class);
-            if (shape == null) {
-                ShapeBuilder shapeBuilder = ShapeParser.parse(context.parser(), this);
-                if (shapeBuilder == null) {
-                    return;
-                }
-                shape = shapeBuilder.buildS4J();
-            }
-            if (fieldType().pointsOnly() == true) {
-                // index configured for pointsOnly
-                if (shape instanceof XShapeCollection && XShapeCollection.class.cast(shape).pointsOnly()) {
-                    // MULTIPOINT data: index each point separately
-                    List<Shape> shapes = ((XShapeCollection) shape).getShapes();
-                    for (Shape s : shapes) {
-                        indexShape(context, s);
-                    }
-                    return;
-                } else if (shape instanceof Point == false) {
-                    throw new MapperParsingException("[{" + fieldType().name() + "}] is configured for points only but a "
-                        + ((shape instanceof JtsGeometry) ? ((JtsGeometry)shape).getGeom().getGeometryType() : shape.getClass())
-                        + " was found");
-                }
-            }
-            indexShape(context, shape);
-        } catch (Exception e) {
-            if (ignoreMalformed.value() == false) {
-                throw new MapperParsingException("failed to parse field [{}] of type [{}]", e, fieldType().name(),
-                    fieldType().typeName());
-            }
-            context.addIgnoredField(fieldType.name());
-        }
-    }
-
-    private void indexShape(ParseContext context, Shape shape) {
-        List<IndexableField> fields = new ArrayList<>(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(shape)));
-        createFieldNamesField(context, fields);
-        for (IndexableField field : fields) {
-            context.doc().add(field);
-        }
-    }
-
-    @Override
-    protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
-        super.doXContentBody(builder, includeDefaults, params);
-
-        if (includeDefaults || fieldType().tree().equals(DeprecatedParameters.Defaults.TREE) == false) {
-            builder.field(DeprecatedParameters.Names.TREE.getPreferredName(), fieldType().tree());
-        }
-
-        if (fieldType().treeLevels() != 0) {
-            builder.field(DeprecatedParameters.Names.TREE_LEVELS.getPreferredName(), fieldType().treeLevels());
-        } else if(includeDefaults && fieldType().precisionInMeters() == -1) { // defaults only make sense if precision is not specified
-            if (DeprecatedParameters.PrefixTrees.GEOHASH.equals(fieldType().tree())) {
-                builder.field(DeprecatedParameters.Names.TREE_LEVELS.getPreferredName(),
-                    DeprecatedParameters.Defaults.GEOHASH_TREE_LEVELS);
-            } else if (DeprecatedParameters.PrefixTrees.LEGACY_QUADTREE.equals(fieldType().tree())) {
-                builder.field(DeprecatedParameters.Names.TREE_LEVELS.getPreferredName(),
-                    DeprecatedParameters.Defaults.QUADTREE_LEVELS);
-            } else if (DeprecatedParameters.PrefixTrees.QUADTREE.equals(fieldType().tree())) {
-                builder.field(DeprecatedParameters.Names.TREE_LEVELS.getPreferredName(),
-                    DeprecatedParameters.Defaults.QUADTREE_LEVELS);
-            } else {
-                throw new IllegalArgumentException("Unknown prefix tree type [" + fieldType().tree() + "]");
-            }
-        }
-        if (fieldType().precisionInMeters() != -1) {
-            builder.field(DeprecatedParameters.Names.PRECISION.getPreferredName(),
-                DistanceUnit.METERS.toString(fieldType().precisionInMeters()));
-        } else if (includeDefaults && fieldType().treeLevels() == 0) { // defaults only make sense if tree levels are not specified
-            builder.field(DeprecatedParameters.Names.PRECISION.getPreferredName(),
-                DistanceUnit.METERS.toString(50));
-        }
-
-        builder.field(DeprecatedParameters.Names.STRATEGY.getPreferredName(), fieldType().strategy().getStrategyName());
-
-        if (includeDefaults || fieldType().distanceErrorPct() != fieldType().defaultDistanceErrorPct) {
-            builder.field(DeprecatedParameters.Names.DISTANCE_ERROR_PCT.getPreferredName(), fieldType().distanceErrorPct());
-        }
-        if (fieldType().strategy() == SpatialStrategy.TERM) {
-            // For TERMs strategy the defaults for points only change to true
-            if (includeDefaults || fieldType().pointsOnly() != true) {
-                builder.field(DeprecatedParameters.Names.POINTS_ONLY.getPreferredName(), fieldType().pointsOnly());
-            }
-        } else {
-            if (includeDefaults || fieldType().pointsOnly() != DeprecatedParameters.Defaults.POINTS_ONLY) {
-                builder.field(DeprecatedParameters.Names.POINTS_ONLY.getPreferredName(), fieldType().pointsOnly());
-            }
-        }
-    }
-}

+ 25 - 92
server/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java

@@ -19,10 +19,6 @@
 
 package org.elasticsearch.index.query;
 
-import org.apache.lucene.document.LatLonShape;
-import org.apache.lucene.geo.Line;
-import org.apache.lucene.geo.Polygon;
-import org.apache.lucene.geo.Rectangle;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.ConstantScoreQuery;
@@ -40,9 +36,8 @@ import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.client.Client;
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.ParsingException;
-import org.elasticsearch.common.geo.GeoPoint;
-import org.elasticsearch.common.geo.GeoShapeType;
 import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.ShapesAvailability;
 import org.elasticsearch.common.geo.SpatialStrategy;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.geo.parsers.ShapeParser;
@@ -53,8 +48,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
-import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
 
 import java.io.IOException;
@@ -335,9 +329,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
         if (relation == null) {
             throw new IllegalArgumentException("No Shape Relation defined");
         }
-        if (SpatialStrategy.TERM.equals(strategy) && relation != ShapeRelation.INTERSECTS) {
+        if (strategy != null && strategy == SpatialStrategy.TERM && relation != ShapeRelation.INTERSECTS) {
             throw new IllegalArgumentException("current strategy [" + strategy.getStrategyName() + "] only supports relation ["
-                + ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]");
+                    + ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]");
         }
         this.relation = relation;
         return this;
@@ -382,96 +376,32 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
             } else {
                 throw new QueryShardException(context, "failed to find geo_shape field [" + fieldName + "]");
             }
-        } else if (fieldType.typeName().equals(BaseGeoShapeFieldMapper.CONTENT_TYPE) == false) {
+        } else if (fieldType.typeName().equals(GeoShapeFieldMapper.CONTENT_TYPE) == false) {
             throw new QueryShardException(context,
                     "Field [" + fieldName + "] is not of type [geo_shape] but of type [" + fieldType.typeName() + "]");
         }
 
-        final BaseGeoShapeFieldMapper.BaseGeoShapeFieldType ft = (BaseGeoShapeFieldMapper.BaseGeoShapeFieldType) fieldType;
-        Query query;
-        if (strategy != null || ft instanceof LegacyGeoShapeFieldMapper.GeoShapeFieldType) {
-            LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (LegacyGeoShapeFieldMapper.GeoShapeFieldType) ft;
-            SpatialStrategy spatialStrategy = shapeFieldType.strategy();
-            if (this.strategy != null) {
-                spatialStrategy = this.strategy;
-            }
-            PrefixTreeStrategy prefixTreeStrategy = shapeFieldType.resolvePrefixTreeStrategy(spatialStrategy);
-            if (prefixTreeStrategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) {
-                // this strategy doesn't support disjoint anymore: but it did
-                // before, including creating lucene fieldcache (!)
-                // in this case, execute disjoint as exists && !intersects
-                BooleanQuery.Builder bool = new BooleanQuery.Builder();
-                Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
-                Query intersects = prefixTreeStrategy.makeQuery(getArgs(shapeToQuery, ShapeRelation.INTERSECTS));
-                bool.add(exists, BooleanClause.Occur.MUST);
-                bool.add(intersects, BooleanClause.Occur.MUST_NOT);
-                query = new ConstantScoreQuery(bool.build());
-            } else {
-                query = new ConstantScoreQuery(prefixTreeStrategy.makeQuery(getArgs(shapeToQuery, relation)));
-            }
-        } else {
-            query = new ConstantScoreQuery(getVectorQuery(context, shapeToQuery));
-        }
-        return query;
-    }
+        final GeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (GeoShapeFieldMapper.GeoShapeFieldType) fieldType;
 
-    private Query getVectorQuery(QueryShardContext context, ShapeBuilder queryShapeBuilder) {
-        // CONTAINS queries are not yet supported by VECTOR strategy
-        if (relation == ShapeRelation.CONTAINS) {
-            throw new QueryShardException(context,
-                ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "]");
+        PrefixTreeStrategy strategy = shapeFieldType.defaultStrategy();
+        if (this.strategy != null) {
+            strategy = shapeFieldType.resolveStrategy(this.strategy);
         }
-
-        // wrap geoQuery as a ConstantScoreQuery
-        return getVectorQueryFromShape(context, queryShapeBuilder.buildLucene());
-    }
-
-    private Query getVectorQueryFromShape(QueryShardContext context, Object queryShape) {
-        Query geoQuery;
-        if (queryShape instanceof Line[]) {
-            geoQuery = LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), (Line[]) queryShape);
-        } else if (queryShape instanceof Polygon[]) {
-            geoQuery = LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), (Polygon[]) queryShape);
-        } else if (queryShape instanceof Line) {
-            geoQuery = LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), (Line) queryShape);
-        } else if (queryShape instanceof Polygon) {
-            geoQuery = LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), (Polygon) queryShape);
-        } else if (queryShape instanceof Rectangle) {
-            Rectangle r = (Rectangle) queryShape;
-            geoQuery = LatLonShape.newBoxQuery(fieldName(), relation.getLuceneRelation(),
-                r.minLat, r.maxLat, r.minLon, r.maxLon);
-        } else if (queryShape instanceof double[][]) {
-            // note: we decompose point queries into a bounding box query with min values == max values
-            // to do this for multipoint we would have to create a BooleanQuery for each point
-            // this is *way* too costly. So we do not allow multipoint queries
-            throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.MULTIPOINT + " queries");
-        } else if (queryShape instanceof double[] || queryShape instanceof GeoPoint) {
-            // for now just create a single bounding box query with min values == max values
-            double[] pt;
-            if (queryShape instanceof GeoPoint) {
-                pt = new double[] {((GeoPoint)queryShape).lon(), ((GeoPoint)queryShape).lat()};
-            } else {
-                pt = (double[])queryShape;
-                if (pt.length != 2) {
-                    throw new QueryShardException(context, "Expected double array of length 2. "
-                        + "But found length " + pt.length + " for field [" + fieldName + "]");
-                }
-            }
-            return LatLonShape.newBoxQuery(fieldName, relation.getLuceneRelation(), pt[1], pt[1], pt[0], pt[0]);
-        } else if (queryShape instanceof Object[]) {
-            geoQuery = createGeometryCollectionQuery(context, (Object[]) queryShape);
+        Query query;
+        if (strategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) {
+            // this strategy doesn't support disjoint anymore: but it did
+            // before, including creating lucene fieldcache (!)
+            // in this case, execute disjoint as exists && !intersects
+            BooleanQuery.Builder bool = new BooleanQuery.Builder();
+            Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
+            Query intersects = strategy.makeQuery(getArgs(shapeToQuery, ShapeRelation.INTERSECTS));
+            bool.add(exists, BooleanClause.Occur.MUST);
+            bool.add(intersects, BooleanClause.Occur.MUST_NOT);
+            query = new ConstantScoreQuery(bool.build());
         } else {
-            throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape");
+            query = new ConstantScoreQuery(strategy.makeQuery(getArgs(shapeToQuery, relation)));
         }
-        return geoQuery;
-    }
-
-    private Query createGeometryCollectionQuery(QueryShardContext context, Object... shapes) {
-        BooleanQuery.Builder bqb = new BooleanQuery.Builder();
-        for (Object shape : shapes) {
-            bqb.add(getVectorQueryFromShape(context, shape), BooleanClause.Occur.SHOULD);
-        }
-        return bqb.build();
+        return query;
     }
 
     /**
@@ -484,6 +414,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
      *            Shape itself is located
      */
     private void fetch(Client client, GetRequest getRequest, String path, ActionListener<ShapeBuilder> listener) {
+        if (ShapesAvailability.JTS_AVAILABLE == false) {
+            throw new IllegalStateException("JTS not available");
+        }
         getRequest.preference("_local");
         client.get(getRequest, new ActionListener<GetResponse>(){
 

+ 6 - 2
server/src/main/java/org/elasticsearch/indices/IndicesModule.java

@@ -25,13 +25,13 @@ import org.elasticsearch.action.admin.indices.rollover.MaxDocsCondition;
 import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition;
 import org.elasticsearch.action.resync.TransportResyncReplicationAction;
 import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.geo.ShapesAvailability;
 import org.elasticsearch.common.inject.AbstractModule;
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
 import org.elasticsearch.common.xcontent.NamedXContentRegistry;
 import org.elasticsearch.index.IndexSettings;
 import org.elasticsearch.index.engine.EngineFactory;
-import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.BinaryFieldMapper;
 import org.elasticsearch.index.mapper.BooleanFieldMapper;
 import org.elasticsearch.index.mapper.CompletionFieldMapper;
@@ -39,6 +39,7 @@ import org.elasticsearch.index.mapper.DateFieldMapper;
 import org.elasticsearch.index.mapper.FieldAliasMapper;
 import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
 import org.elasticsearch.index.mapper.GeoPointFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.IdFieldMapper;
 import org.elasticsearch.index.mapper.IgnoredFieldMapper;
 import org.elasticsearch.index.mapper.IndexFieldMapper;
@@ -131,7 +132,10 @@ public class IndicesModule extends AbstractModule {
         mappers.put(CompletionFieldMapper.CONTENT_TYPE, new CompletionFieldMapper.TypeParser());
         mappers.put(FieldAliasMapper.CONTENT_TYPE, new FieldAliasMapper.TypeParser());
         mappers.put(GeoPointFieldMapper.CONTENT_TYPE, new GeoPointFieldMapper.TypeParser());
-        mappers.put(BaseGeoShapeFieldMapper.CONTENT_TYPE, new BaseGeoShapeFieldMapper.TypeParser());
+
+        if (ShapesAvailability.JTS_AVAILABLE && ShapesAvailability.SPATIAL4J_AVAILABLE) {
+            mappers.put(GeoShapeFieldMapper.CONTENT_TYPE, new GeoShapeFieldMapper.TypeParser());
+        }
 
         for (MapperPlugin mapperPlugin : mapperPlugins) {
             for (Map.Entry<String, Mapper.TypeParser> entry : mapperPlugin.getMappers().entrySet()) {

+ 5 - 3
server/src/test/java/org/elasticsearch/common/geo/GeoJsonShapeParserTests.java

@@ -32,7 +32,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
 import org.elasticsearch.index.mapper.ContentPath;
-import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.Mapper;
 import org.elasticsearch.test.VersionUtils;
 import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions;
@@ -296,8 +296,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
         LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
         Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
         Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
-        final LegacyGeoShapeFieldMapper mapperBuilder =
-            (LegacyGeoShapeFieldMapper) (new LegacyGeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext));
+        final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
         try (XContentParser parser = createParser(polygonGeoJson)) {
             parser.nextToken();
             ElasticsearchGeoAssertions.assertEquals(jtsGeom(expected), ShapeParser.parse(parser, mapperBuilder).buildS4J());
@@ -897,6 +896,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
                         .startArray().value(101.0).value(1.0).endArray()
                     .endArray()
                 .endObject();
+
         ShapeCollection<?> expected = shapeCollection(
             SPATIAL_CONTEXT.makePoint(100, 0),
             SPATIAL_CONTEXT.makePoint(101, 1.0));
@@ -968,6 +968,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
         shellCoordinates.add(new Coordinate(102, 2));
         shellCoordinates.add(new Coordinate(102, 3));
 
+
         shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
         Polygon withoutHoles = GEOMETRY_FACTORY.createPolygon(shell, null);
 
@@ -1148,6 +1149,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
                     .startObject("nested").startArray("coordinates").value(200.0).value(0.0).endArray().endObject()
                     .startObject("lala").field("type", "NotAPoint").endObject()
                 .endObject();
+
             Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0));
             assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson, true);
 

+ 7 - 12
server/src/test/java/org/elasticsearch/common/geo/GeoWKTShapeParserTests.java

@@ -43,7 +43,6 @@ import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.mapper.ContentPath;
 import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
-import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.Mapper;
 import org.elasticsearch.test.geo.RandomShapeGenerator;
 import org.locationtech.jts.geom.Coordinate;
@@ -147,6 +146,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
     @Override
     public void testParseLineString() throws IOException {
         List<Coordinate> coordinates = randomLineStringCoords();
+
         LineString expected = GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[coordinates.size()]));
         assertExpected(jtsGeom(expected), new LineStringBuilder(coordinates), true);
 
@@ -279,14 +279,13 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
         parser.nextToken();
 
         Settings indexSettings = Settings.builder()
-            .put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_7_0_0)
+            .put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_6_3_0)
             .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
             .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
             .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
 
         Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
-        final GeoShapeFieldMapper mapperBuilder =
-            (GeoShapeFieldMapper) (new GeoShapeFieldMapper.Builder("test").ignoreZValue(false).build(mockBuilderContext));
+        final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(false).build(mockBuilderContext);
 
         // test store z disabled
         ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class,
@@ -324,8 +323,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
             .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
 
         Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
-        final LegacyGeoShapeFieldMapper mapperBuilder =
-            (LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext));
+        final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
 
         // test store z disabled
         ElasticsearchException e = expectThrows(ElasticsearchException.class,
@@ -354,8 +352,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
             .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
 
         Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
-        final LegacyGeoShapeFieldMapper mapperBuilder =
-            (LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext));
+        final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
 
         ShapeBuilder<?, ?> shapeBuilder = ShapeParser.parse(parser, mapperBuilder);
         assertEquals(shapeBuilder.numDimensions(), 3);
@@ -375,14 +372,12 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
             .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
 
         Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
-        final LegacyGeoShapeFieldMapper defaultMapperBuilder =
-            (LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").coerce(false).build(mockBuilderContext));
+        final GeoShapeFieldMapper defaultMapperBuilder = new GeoShapeFieldMapper.Builder("test").coerce(false).build(mockBuilderContext);
         ElasticsearchParseException exception = expectThrows(ElasticsearchParseException.class,
             () -> ShapeParser.parse(parser, defaultMapperBuilder));
         assertEquals("invalid LinearRing found (coordinates are not closed)", exception.getMessage());
 
-        final LegacyGeoShapeFieldMapper coercingMapperBuilder =
-            (LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").coerce(true).build(mockBuilderContext));
+        final GeoShapeFieldMapper coercingMapperBuilder = new GeoShapeFieldMapper.Builder("test").coerce(true).build(mockBuilderContext);
         ShapeBuilder<?, ?> shapeBuilder = ShapeParser.parse(parser, coercingMapperBuilder);
         assertNotNull(shapeBuilder);
         assertEquals("polygon ((100.0 5.0, 100.0 10.0, 90.0 10.0, 90.0 5.0, 100.0 5.0))", shapeBuilder.toWKT());

+ 7 - 14
server/src/test/java/org/elasticsearch/index/mapper/ExternalMapper.java

@@ -24,8 +24,8 @@ import org.apache.lucene.index.Term;
 import org.apache.lucene.search.DocValuesFieldExistsQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TermQuery;
-import org.elasticsearch.Version;
 import org.elasticsearch.common.geo.builders.PointBuilder;
+import org.locationtech.spatial4j.shape.Point;
 import org.elasticsearch.common.collect.Iterators;
 import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.common.settings.Settings;
@@ -63,7 +63,6 @@ public class ExternalMapper extends FieldMapper {
         private BooleanFieldMapper.Builder boolBuilder = new BooleanFieldMapper.Builder(Names.FIELD_BOOL);
         private GeoPointFieldMapper.Builder latLonPointBuilder = new GeoPointFieldMapper.Builder(Names.FIELD_POINT);
         private GeoShapeFieldMapper.Builder shapeBuilder = new GeoShapeFieldMapper.Builder(Names.FIELD_SHAPE);
-        private LegacyGeoShapeFieldMapper.Builder legacyShapeBuilder = new LegacyGeoShapeFieldMapper.Builder(Names.FIELD_SHAPE);
         private Mapper.Builder stringBuilder;
         private String generatedValue;
         private String mapperName;
@@ -87,9 +86,7 @@ public class ExternalMapper extends FieldMapper {
             BinaryFieldMapper binMapper = binBuilder.build(context);
             BooleanFieldMapper boolMapper = boolBuilder.build(context);
             GeoPointFieldMapper pointMapper = latLonPointBuilder.build(context);
-            BaseGeoShapeFieldMapper shapeMapper = (context.indexCreatedVersion().before(Version.V_6_6_0))
-                ? legacyShapeBuilder.build(context)
-                : shapeBuilder.build(context);
+            GeoShapeFieldMapper shapeMapper = shapeBuilder.build(context);
             FieldMapper stringMapper = (FieldMapper)stringBuilder.build(context);
             context.path().remove();
 
@@ -153,13 +150,13 @@ public class ExternalMapper extends FieldMapper {
     private BinaryFieldMapper binMapper;
     private BooleanFieldMapper boolMapper;
     private GeoPointFieldMapper pointMapper;
-    private BaseGeoShapeFieldMapper shapeMapper;
+    private GeoShapeFieldMapper shapeMapper;
     private FieldMapper stringMapper;
 
     public ExternalMapper(String simpleName, MappedFieldType fieldType,
                           String generatedValue, String mapperName,
                           BinaryFieldMapper binMapper, BooleanFieldMapper boolMapper, GeoPointFieldMapper pointMapper,
-                          BaseGeoShapeFieldMapper shapeMapper, FieldMapper stringMapper, Settings indexSettings,
+                          GeoShapeFieldMapper shapeMapper, FieldMapper stringMapper, Settings indexSettings,
                           MultiFields multiFields, CopyTo copyTo) {
         super(simpleName, fieldType, new ExternalFieldType(), indexSettings, multiFields, copyTo);
         this.generatedValue = generatedValue;
@@ -185,12 +182,8 @@ public class ExternalMapper extends FieldMapper {
         pointMapper.parse(context.createExternalValueContext(point));
 
         // Let's add a Dummy Shape
-        PointBuilder pb = new PointBuilder(-100, 45);
-        if (shapeMapper instanceof GeoShapeFieldMapper) {
-            shapeMapper.parse(context.createExternalValueContext(pb.buildLucene()));
-        } else {
-            shapeMapper.parse(context.createExternalValueContext(pb.buildS4J()));
-        }
+        Point shape = new PointBuilder(-100, 45).buildS4J();
+        shapeMapper.parse(context.createExternalValueContext(shape));
 
         context = context.createExternalValueContext(generatedValue);
 
@@ -217,7 +210,7 @@ public class ExternalMapper extends FieldMapper {
         BinaryFieldMapper binMapperUpdate = (BinaryFieldMapper) binMapper.updateFieldType(fullNameToFieldType);
         BooleanFieldMapper boolMapperUpdate = (BooleanFieldMapper) boolMapper.updateFieldType(fullNameToFieldType);
         GeoPointFieldMapper pointMapperUpdate = (GeoPointFieldMapper) pointMapper.updateFieldType(fullNameToFieldType);
-        BaseGeoShapeFieldMapper shapeMapperUpdate = (BaseGeoShapeFieldMapper) shapeMapper.updateFieldType(fullNameToFieldType);
+        GeoShapeFieldMapper shapeMapperUpdate = (GeoShapeFieldMapper) shapeMapper.updateFieldType(fullNameToFieldType);
         TextFieldMapper stringMapperUpdate = (TextFieldMapper) stringMapper.updateFieldType(fullNameToFieldType);
         if (update == this
                 && multiFieldsUpdate == multiFields

+ 2 - 4
server/src/test/java/org/elasticsearch/index/mapper/ExternalValuesMapperIntegrationIT.java

@@ -21,13 +21,12 @@ package org.elasticsearch.index.mapper;
 
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
+import org.elasticsearch.common.geo.builders.PointBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
 import org.elasticsearch.test.ESIntegTestCase;
-import org.locationtech.jts.geom.Coordinate;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -119,8 +118,7 @@ public class ExternalValuesMapperIntegrationIT extends ESIntegTestCase {
         assertThat(response.getHits().getTotalHits().value, equalTo((long) 1));
 
         response = client().prepareSearch("test-idx")
-                .setPostFilter(QueryBuilders.geoShapeQuery("field.shape",
-                    new EnvelopeBuilder(new Coordinate(-101, 46), new Coordinate(-99, 44))).relation(ShapeRelation.WITHIN))
+                .setPostFilter(QueryBuilders.geoShapeQuery("field.shape", new PointBuilder(-100, 45)).relation(ShapeRelation.WITHIN))
                         .execute().actionGet();
 
         assertThat(response.getHits().getTotalHits().value, equalTo((long) 1));

+ 410 - 42
server/src/test/java/org/elasticsearch/index/mapper/GeoShapeFieldMapperTests.java

@@ -18,9 +18,14 @@
  */
 package org.elasticsearch.index.mapper;
 
+import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
+import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
+import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
+import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
 import org.elasticsearch.common.Explicit;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.compress.CompressedXContent;
+import org.elasticsearch.common.geo.GeoUtils;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -37,6 +42,7 @@ import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.not;
 
 public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
 
@@ -47,10 +53,10 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
 
     public void testDefaultConfiguration() throws IOException {
         String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .endObject().endObject()
-            .endObject().endObject());
+                .startObject("properties").startObject("location")
+                    .field("type", "geo_shape")
+                .endObject().endObject()
+                .endObject().endObject());
 
         DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
             .parse("type1", new CompressedXContent(mapping));
@@ -58,8 +64,12 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
         assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
 
         GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
-        assertThat(geoShapeFieldMapper.fieldType().orientation(),
-            equalTo(GeoShapeFieldMapper.Defaults.ORIENTATION.value()));
+        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+        assertThat(strategy.getDistErrPct(), equalTo(0.025d));
+        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+        assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoShapeFieldMapper.Defaults.GEOHASH_LEVELS));
+        assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(GeoShapeFieldMapper.Defaults.ORIENTATION));
     }
 
     /**
@@ -67,11 +77,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
      */
     public void testOrientationParsing() throws IOException {
         String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("orientation", "left")
-            .endObject().endObject()
-            .endObject().endObject());
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("orientation", "left")
+                .endObject().endObject()
+                .endObject().endObject());
 
         DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
             .parse("type1", new CompressedXContent(mapping));
@@ -85,11 +95,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
 
         // explicit right orientation test
         mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("orientation", "right")
-            .endObject().endObject()
-            .endObject().endObject());
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("orientation", "right")
+                .endObject().endObject()
+                .endObject().endObject());
 
         defaultMapper = createIndex("test2").mapperService().documentMapperParser()
             .parse("type1", new CompressedXContent(mapping));
@@ -107,11 +117,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
      */
     public void testCoerceParsing() throws IOException {
         String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("coerce", "true")
-            .endObject().endObject()
-            .endObject().endObject());
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("coerce", "true")
+                .endObject().endObject()
+                .endObject().endObject());
 
         DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
             .parse("type1", new CompressedXContent(mapping));
@@ -123,11 +133,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
 
         // explicit false coerce test
         mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("coerce", "false")
-            .endObject().endObject()
-            .endObject().endObject());
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("coerce", "false")
+                .endObject().endObject()
+                .endObject().endObject());
 
         defaultMapper = createIndex("test2").mapperService().documentMapperParser()
             .parse("type1", new CompressedXContent(mapping));
@@ -136,7 +146,6 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
 
         coerce = ((GeoShapeFieldMapper)fieldMapper).coerce().value();
         assertThat(coerce, equalTo(false));
-        assertFieldWarnings("tree");
     }
 
 
@@ -213,45 +222,304 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
         assertThat(ignoreMalformed.value(), equalTo(false));
     }
 
+    public void testGeohashConfiguration() throws IOException {
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                    .field("type", "geo_shape")
+                    .field("tree", "geohash")
+                    .field("tree_levels", "4")
+                    .field("distance_error_pct", "0.1")
+                .endObject().endObject()
+                .endObject().endObject());
+
+        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
+            .parse("type1", new CompressedXContent(mapping));
+        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+        assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+        GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+        assertThat(strategy.getDistErrPct(), equalTo(0.1));
+        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+        assertThat(strategy.getGrid().getMaxLevels(), equalTo(4));
+    }
+
+    public void testQuadtreeConfiguration() throws IOException {
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                    .field("type", "geo_shape")
+                    .field("tree", "quadtree")
+                    .field("tree_levels", "6")
+                    .field("distance_error_pct", "0.5")
+                    .field("points_only", true)
+                .endObject().endObject()
+                .endObject().endObject());
+
+        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
+            .parse("type1", new CompressedXContent(mapping));
+        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+        assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+        GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+        assertThat(strategy.getDistErrPct(), equalTo(0.5));
+        assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
+        assertThat(strategy.getGrid().getMaxLevels(), equalTo(6));
+        assertThat(strategy.isPointsOnly(), equalTo(true));
+    }
+
+    public void testLevelPrecisionConfiguration() throws IOException {
+        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
+
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                    .startObject("properties").startObject("location")
+                        .field("type", "geo_shape")
+                        .field("tree", "quadtree")
+                        .field("tree_levels", "6")
+                        .field("precision", "70m")
+                        .field("distance_error_pct", "0.5")
+                    .endObject().endObject()
+                    .endObject().endObject());
+
+
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+            assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+            GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+            assertThat(strategy.getDistErrPct(), equalTo(0.5));
+            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
+            // 70m is more precise so it wins
+            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(70d)));
+        }
+
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                    .startObject("properties").startObject("location")
+                    .field("type", "geo_shape")
+                    .field("tree", "quadtree")
+                    .field("tree_levels", "26")
+                    .field("precision", "70m")
+                    .endObject().endObject()
+                    .endObject().endObject());
+
+
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+            assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+            GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+            // distance_error_pct was not specified so we expect the mapper to take the highest precision between "precision" and
+            // "tree_levels" setting distErrPct to 0 to guarantee desired precision
+            assertThat(strategy.getDistErrPct(), equalTo(0.0));
+            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
+            // 70m is less precise so it loses
+            assertThat(strategy.getGrid().getMaxLevels(), equalTo(26));
+        }
+
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                    .startObject("properties").startObject("location")
+                        .field("type", "geo_shape")
+                        .field("tree", "geohash")
+                        .field("tree_levels", "6")
+                        .field("precision", "70m")
+                        .field("distance_error_pct", "0.5")
+                    .endObject().endObject()
+                    .endObject().endObject());
+
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+            assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+            GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+            assertThat(strategy.getDistErrPct(), equalTo(0.5));
+            assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+            // 70m is more precise so it wins
+            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(70d)));
+        }
+
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                    .startObject("properties").startObject("location")
+                        .field("type", "geo_shape")
+                        .field("tree", "geohash")
+                        .field("tree_levels",  GeoUtils.geoHashLevelsForPrecision(70d)+1)
+                        .field("precision", "70m")
+                        .field("distance_error_pct", "0.5")
+                    .endObject().endObject()
+                    .endObject().endObject());
+
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+            assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+            GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+            assertThat(strategy.getDistErrPct(), equalTo(0.5));
+            assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+            assertThat(strategy.getGrid().getMaxLevels(),  equalTo(GeoUtils.geoHashLevelsForPrecision(70d)+1));
+        }
+
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                    .startObject("properties").startObject("location")
+                        .field("type", "geo_shape")
+                        .field("tree", "quadtree")
+                        .field("tree_levels", GeoUtils.quadTreeLevelsForPrecision(70d)+1)
+                        .field("precision", "70m")
+                        .field("distance_error_pct", "0.5")
+                    .endObject().endObject()
+                    .endObject().endObject());
+
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+            assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+            GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+            assertThat(strategy.getDistErrPct(), equalTo(0.5));
+            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
+            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(70d)+1));
+        }
+    }
+
+    public void testPointsOnlyOption() throws IOException {
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "geohash")
+                .field("points_only", true)
+                .endObject().endObject()
+                .endObject().endObject());
+
+        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
+            .parse("type1", new CompressedXContent(mapping));
+        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+        assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+        GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+        assertThat(strategy.isPointsOnly(), equalTo(true));
+    }
+
+    public void testLevelDefaults() throws IOException {
+        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                    .startObject("properties").startObject("location")
+                        .field("type", "geo_shape")
+                        .field("tree", "quadtree")
+                        .field("distance_error_pct", "0.5")
+                    .endObject().endObject()
+                    .endObject().endObject());
+
 
-    private void assertFieldWarnings(String... fieldNames) {
-        String[] warnings = new String[fieldNames.length];
-        for (int i = 0; i < fieldNames.length; ++i) {
-            warnings[i] = "Field parameter [" + fieldNames[i] + "] "
-                + "is deprecated and will be removed in a future version.";
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+            assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+            GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+            assertThat(strategy.getDistErrPct(), equalTo(0.5));
+            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
+            /* 50m is default */
+            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(50d)));
+        }
+
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                    .startObject("properties").startObject("location")
+                        .field("type", "geo_shape")
+                        .field("tree", "geohash")
+                        .field("distance_error_pct", "0.5")
+                    .endObject().endObject()
+                    .endObject().endObject());
+
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+            assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+            GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+            assertThat(strategy.getDistErrPct(), equalTo(0.5));
+            assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+            /* 50m is default */
+            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(50d)));
         }
     }
 
     public void testGeoShapeMapperMerge() throws Exception {
         String stage1Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")
-            .startObject("shape").field("type", "geo_shape")
-            .field("orientation", "ccw")
-            .endObject().endObject().endObject().endObject());
+                .startObject("shape").field("type", "geo_shape").field("tree", "geohash")
+                .field("strategy", "recursive")
+                .field("precision", "1m").field("tree_levels", 8).field("distance_error_pct", 0.01)
+                .field("orientation", "ccw")
+                .endObject().endObject().endObject().endObject());
         MapperService mapperService = createIndex("test").mapperService();
         DocumentMapper docMapper = mapperService.merge("type", new CompressedXContent(stage1Mapping),
             MapperService.MergeReason.MAPPING_UPDATE);
         String stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
-            .startObject("properties").startObject("shape").field("type", "geo_shape")
-            .field("orientation", "cw").endObject().endObject().endObject().endObject());
-        mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
+                .startObject("properties").startObject("shape").field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .field("strategy", "term").field("precision", "1km")
+                .field("tree_levels", 26).field("distance_error_pct", 26)
+                .field("orientation", "cw").endObject().endObject().endObject().endObject());
+        try {
+            mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), containsString("mapper [shape] has different [strategy]"));
+            assertThat(e.getMessage(), containsString("mapper [shape] has different [tree]"));
+            assertThat(e.getMessage(), containsString("mapper [shape] has different [tree_levels]"));
+            assertThat(e.getMessage(), containsString("mapper [shape] has different [precision]"));
+        }
 
         // verify nothing changed
         Mapper fieldMapper = docMapper.mappers().getMapper("shape");
         assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
 
         GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+        assertThat(strategy, instanceOf(RecursivePrefixTreeStrategy.class));
+        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+        assertThat(strategy.getDistErrPct(), equalTo(0.01));
+        assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(1d)));
         assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(ShapeBuilder.Orientation.CCW));
 
-        // change mapping; orientation
+        // correct mapping
         stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
-            .startObject("properties").startObject("shape").field("type", "geo_shape")
-            .field("orientation", "cw").endObject().endObject().endObject().endObject());
+                .startObject("properties").startObject("shape").field("type", "geo_shape").field("precision", "1m")
+                .field("tree_levels", 8).field("distance_error_pct", 0.001)
+                .field("orientation", "cw").endObject().endObject().endObject().endObject());
         docMapper = mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
 
         fieldMapper = docMapper.mappers().getMapper("shape");
         assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
 
         geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+        strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+        assertThat(strategy, instanceOf(RecursivePrefixTreeStrategy.class));
+        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
+        assertThat(strategy.getDistErrPct(), equalTo(0.001));
+        assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(1d)));
         assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(ShapeBuilder.Orientation.CW));
     }
 
@@ -276,12 +544,112 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
             String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
                 .startObject("properties").startObject("location")
                 .field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .endObject().endObject()
+                .endObject().endObject());
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
+            assertTrue(serialized, serialized.contains("\"precision\":\"50.0m\""));
+            assertTrue(serialized, serialized.contains("\"tree_levels\":21"));
+        }
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "geohash")
+                .endObject().endObject()
+                .endObject().endObject());
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
+            assertTrue(serialized, serialized.contains("\"precision\":\"50.0m\""));
+            assertTrue(serialized, serialized.contains("\"tree_levels\":9"));
+        }
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .field("tree_levels", "6")
+                .endObject().endObject()
+                .endObject().endObject());
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
+            assertFalse(serialized, serialized.contains("\"precision\":"));
+            assertTrue(serialized, serialized.contains("\"tree_levels\":6"));
+        }
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .field("precision", "6")
                 .endObject().endObject()
                 .endObject().endObject());
             DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
             String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
-            assertTrue(serialized, serialized.contains("\"orientation\":\"" + BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value() + "\""));
+            assertTrue(serialized, serialized.contains("\"precision\":\"6.0m\""));
+            assertFalse(serialized, serialized.contains("\"tree_levels\":"));
         }
+        {
+            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .field("precision", "6m")
+                .field("tree_levels", "5")
+                .endObject().endObject()
+                .endObject().endObject());
+            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
+            String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
+            assertTrue(serialized, serialized.contains("\"precision\":\"6.0m\""));
+            assertTrue(serialized, serialized.contains("\"tree_levels\":5"));
+        }
+    }
+
+    public void testPointsOnlyDefaultsWithTermStrategy() throws IOException {
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+            .startObject("properties").startObject("location")
+            .field("type", "geo_shape")
+            .field("tree", "quadtree")
+            .field("precision", "10m")
+            .field("strategy", "term")
+            .endObject().endObject()
+            .endObject().endObject());
+
+        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
+            .parse("type1", new CompressedXContent(mapping));
+        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
+        assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+
+        GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
+        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
+
+        assertThat(strategy.getDistErrPct(), equalTo(0.0));
+        assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
+        assertThat(strategy.getGrid().getMaxLevels(), equalTo(23));
+        assertThat(strategy.isPointsOnly(), equalTo(true));
+        // term strategy changes the default for points_only, check that we handle it correctly
+        assertThat(toXContentString(geoShapeFieldMapper, false), not(containsString("points_only")));
+    }
+
+
+    public void testPointsOnlyFalseWithTermStrategy() throws Exception {
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+            .startObject("properties").startObject("location")
+            .field("type", "geo_shape")
+            .field("tree", "quadtree")
+            .field("precision", "10m")
+            .field("strategy", "term")
+            .field("points_only", false)
+            .endObject().endObject()
+            .endObject().endObject());
+
+        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
+
+        IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
+            () -> parser.parse("type1", new CompressedXContent(mapping))
+        );
+        assertThat(e.getMessage(), containsString("points_only cannot be set to false for term strategy"));
     }
 
     public String toXContentString(GeoShapeFieldMapper mapper, boolean includeDefaults) throws IOException {

+ 49 - 3
server/src/test/java/org/elasticsearch/index/mapper/GeoShapeFieldTypeTests.java

@@ -18,23 +18,69 @@
  */
 package org.elasticsearch.index.mapper;
 
+import org.elasticsearch.common.geo.SpatialStrategy;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.index.mapper.GeoShapeFieldMapper.GeoShapeFieldType;
 import org.junit.Before;
 
+import java.io.IOException;
+
 public class GeoShapeFieldTypeTests extends FieldTypeTestCase {
     @Override
     protected MappedFieldType createDefaultFieldType() {
-        return new GeoShapeFieldType();
+        return new GeoShapeFieldMapper.GeoShapeFieldType();
     }
 
     @Before
     public void setupProperties() {
-        addModifier(new FieldTypeTestCase.Modifier("orientation", true) {
+        addModifier(new Modifier("tree", false) {
+            @Override
+            public void modify(MappedFieldType ft) {
+                ((GeoShapeFieldMapper.GeoShapeFieldType)ft).setTree("quadtree");
+            }
+        });
+        addModifier(new Modifier("strategy", false) {
+            @Override
+            public void modify(MappedFieldType ft) {
+                ((GeoShapeFieldMapper.GeoShapeFieldType)ft).setStrategyName("term");
+            }
+        });
+        addModifier(new Modifier("tree_levels", false) {
             @Override
             public void modify(MappedFieldType ft) {
-                ((GeoShapeFieldType)ft).setOrientation(ShapeBuilder.Orientation.LEFT);
+                ((GeoShapeFieldMapper.GeoShapeFieldType)ft).setTreeLevels(10);
             }
         });
+        addModifier(new Modifier("precision", false) {
+            @Override
+            public void modify(MappedFieldType ft) {
+                ((GeoShapeFieldMapper.GeoShapeFieldType)ft).setPrecisionInMeters(20);
+            }
+        });
+        addModifier(new Modifier("distance_error_pct", true) {
+            @Override
+            public void modify(MappedFieldType ft) {
+                ((GeoShapeFieldMapper.GeoShapeFieldType)ft).setDefaultDistanceErrorPct(0.5);
+            }
+        });
+        addModifier(new Modifier("orientation", true) {
+            @Override
+            public void modify(MappedFieldType ft) {
+                ((GeoShapeFieldMapper.GeoShapeFieldType)ft).setOrientation(ShapeBuilder.Orientation.LEFT);
+            }
+        });
+    }
+
+    /**
+     * Test for {@link GeoShapeFieldType#setStrategyName(String)} that checks that {@link GeoShapeFieldType#pointsOnly()}
+     * gets set as a side effect when using SpatialStrategy.TERM
+     */
+    public void testSetStrategyName() throws IOException {
+        GeoShapeFieldType fieldType = new GeoShapeFieldMapper.GeoShapeFieldType();
+        assertFalse(fieldType.pointsOnly());
+        fieldType.setStrategyName(SpatialStrategy.RECURSIVE.getStrategyName());
+        assertFalse(fieldType.pointsOnly());
+        fieldType.setStrategyName(SpatialStrategy.TERM.getStrategyName());
+        assertTrue(fieldType.pointsOnly());
     }
 }

+ 0 - 714
server/src/test/java/org/elasticsearch/index/mapper/LegacyGeoShapeFieldMapperTests.java

@@ -1,714 +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.mapper;
-
-import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
-import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
-import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
-import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
-import org.elasticsearch.ElasticsearchParseException;
-import org.elasticsearch.common.Explicit;
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.compress.CompressedXContent;
-import org.elasticsearch.common.geo.GeoUtils;
-import org.elasticsearch.common.geo.builders.ShapeBuilder;
-import org.elasticsearch.common.xcontent.ToXContent;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.plugins.Plugin;
-import org.elasticsearch.test.ESSingleNodeTestCase;
-import org.elasticsearch.test.InternalSettingsPlugin;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-
-import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.not;
-
-public class LegacyGeoShapeFieldMapperTests extends ESSingleNodeTestCase {
-
-    @Override
-    protected Collection<Class<? extends Plugin>> getPlugins() {
-        return pluginList(InternalSettingsPlugin.class);
-    }
-
-    public void testDefaultConfiguration() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                    .field("type", "geo_shape")
-                    .field("strategy", "recursive")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-        assertThat(geoShapeFieldMapper.fieldType().tree(),
-            equalTo(LegacyGeoShapeFieldMapper.DeprecatedParameters.Defaults.TREE));
-        assertThat(geoShapeFieldMapper.fieldType().treeLevels(),
-            equalTo(LegacyGeoShapeFieldMapper.DeprecatedParameters.Defaults.QUADTREE_LEVELS));
-        assertThat(geoShapeFieldMapper.fieldType().pointsOnly(),
-            equalTo(LegacyGeoShapeFieldMapper.DeprecatedParameters.Defaults.POINTS_ONLY));
-        assertThat(geoShapeFieldMapper.fieldType().distanceErrorPct(),
-            equalTo(LegacyGeoShapeFieldMapper.DeprecatedParameters.Defaults.DISTANCE_ERROR_PCT));
-        assertThat(geoShapeFieldMapper.fieldType().orientation(),
-            equalTo(LegacyGeoShapeFieldMapper.Defaults.ORIENTATION.value()));
-        assertFieldWarnings("strategy");
-    }
-
-    /**
-     * Test that orientation parameter correctly parses
-     */
-    public void testOrientationParsing() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("orientation", "left")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        ShapeBuilder.Orientation orientation = ((LegacyGeoShapeFieldMapper)fieldMapper).fieldType().orientation();
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.CLOCKWISE));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.LEFT));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.CW));
-
-        // explicit right orientation test
-        mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("orientation", "right")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        defaultMapper = createIndex("test2").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        orientation = ((LegacyGeoShapeFieldMapper)fieldMapper).fieldType().orientation();
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.COUNTER_CLOCKWISE));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.RIGHT));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.CCW));
-        assertFieldWarnings("tree");
-    }
-
-    /**
-     * Test that coerce parameter correctly parses
-     */
-    public void testCoerceParsing() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("coerce", "true")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        boolean coerce = ((LegacyGeoShapeFieldMapper)fieldMapper).coerce().value();
-        assertThat(coerce, equalTo(true));
-
-        // explicit false coerce test
-        mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("coerce", "false")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        defaultMapper = createIndex("test2").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        coerce = ((LegacyGeoShapeFieldMapper)fieldMapper).coerce().value();
-        assertThat(coerce, equalTo(false));
-        assertFieldWarnings("tree");
-    }
-
-
-    /**
-     * Test that accept_z_value parameter correctly parses
-     */
-    public void testIgnoreZValue() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("strategy", "recursive")
-            .field(IGNORE_Z_VALUE.getPreferredName(), "true")
-            .endObject().endObject()
-            .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        boolean ignoreZValue = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreZValue().value();
-        assertThat(ignoreZValue, equalTo(true));
-
-        // explicit false accept_z_value test
-        mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("tree", "quadtree")
-            .field(IGNORE_Z_VALUE.getPreferredName(), "false")
-            .endObject().endObject()
-            .endObject().endObject());
-
-        defaultMapper = createIndex("test2").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        ignoreZValue = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreZValue().value();
-        assertThat(ignoreZValue, equalTo(false));
-        assertFieldWarnings("strategy", "tree");
-    }
-
-    /**
-     * Test that ignore_malformed parameter correctly parses
-     */
-    public void testIgnoreMalformedParsing() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("tree", "quadtree")
-            .field("ignore_malformed", "true")
-            .endObject().endObject()
-            .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        Explicit<Boolean> ignoreMalformed = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreMalformed();
-        assertThat(ignoreMalformed.value(), equalTo(true));
-
-        // explicit false ignore_malformed test
-        mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("tree", "quadtree")
-            .field("ignore_malformed", "false")
-            .endObject().endObject()
-            .endObject().endObject());
-
-        defaultMapper = createIndex("test2").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        ignoreMalformed = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreMalformed();
-        assertThat(ignoreMalformed.explicit(), equalTo(true));
-        assertThat(ignoreMalformed.value(), equalTo(false));
-        assertFieldWarnings("tree");
-    }
-
-    public void testGeohashConfiguration() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                    .field("type", "geo_shape")
-                    .field("tree", "geohash")
-                    .field("tree_levels", "4")
-                    .field("distance_error_pct", "0.1")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-        assertThat(strategy.getDistErrPct(), equalTo(0.1));
-        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
-        assertThat(strategy.getGrid().getMaxLevels(), equalTo(4));
-        assertFieldWarnings("tree", "tree_levels", "distance_error_pct");
-    }
-
-    public void testQuadtreeConfiguration() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                    .field("type", "geo_shape")
-                    .field("tree", "quadtree")
-                    .field("tree_levels", "6")
-                    .field("distance_error_pct", "0.5")
-                    .field("points_only", true)
-                .endObject().endObject()
-                .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-        assertThat(strategy.getDistErrPct(), equalTo(0.5));
-        assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
-        assertThat(strategy.getGrid().getMaxLevels(), equalTo(6));
-        assertThat(strategy.isPointsOnly(), equalTo(true));
-        assertFieldWarnings("tree", "tree_levels", "distance_error_pct", "points_only");
-    }
-
-    private void assertFieldWarnings(String... fieldNames) {
-        String[] warnings = new String[fieldNames.length];
-        for (int i = 0; i < fieldNames.length; ++i) {
-            warnings[i] = "Field parameter [" + fieldNames[i] + "] "
-                + "is deprecated and will be removed in a future version.";
-        }
-        assertWarnings(warnings);
-    }
-
-    public void testLevelPrecisionConfiguration() throws IOException {
-        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
-
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                    .startObject("properties").startObject("location")
-                        .field("type", "geo_shape")
-                        .field("tree", "quadtree")
-                        .field("tree_levels", "6")
-                        .field("precision", "70m")
-                        .field("distance_error_pct", "0.5")
-                    .endObject().endObject()
-                    .endObject().endObject());
-
-
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-            assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-            LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-            assertThat(strategy.getDistErrPct(), equalTo(0.5));
-            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
-            // 70m is more precise so it wins
-            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(70d)));
-        }
-
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                    .startObject("properties").startObject("location")
-                    .field("type", "geo_shape")
-                    .field("tree", "quadtree")
-                    .field("tree_levels", "26")
-                    .field("precision", "70m")
-                    .endObject().endObject()
-                    .endObject().endObject());
-
-
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-            assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-            LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-            // distance_error_pct was not specified so we expect the mapper to take the highest precision between "precision" and
-            // "tree_levels" setting distErrPct to 0 to guarantee desired precision
-            assertThat(strategy.getDistErrPct(), equalTo(0.0));
-            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
-            // 70m is less precise so it loses
-            assertThat(strategy.getGrid().getMaxLevels(), equalTo(26));
-        }
-
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                    .startObject("properties").startObject("location")
-                        .field("type", "geo_shape")
-                        .field("tree", "geohash")
-                        .field("tree_levels", "6")
-                        .field("precision", "70m")
-                        .field("distance_error_pct", "0.5")
-                    .endObject().endObject()
-                    .endObject().endObject());
-
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-            assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-            LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-            assertThat(strategy.getDistErrPct(), equalTo(0.5));
-            assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
-            // 70m is more precise so it wins
-            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(70d)));
-        }
-
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                    .startObject("properties").startObject("location")
-                        .field("type", "geo_shape")
-                        .field("tree", "geohash")
-                        .field("tree_levels",  GeoUtils.geoHashLevelsForPrecision(70d)+1)
-                        .field("precision", "70m")
-                        .field("distance_error_pct", "0.5")
-                    .endObject().endObject()
-                    .endObject().endObject());
-
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-            assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-            LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-            assertThat(strategy.getDistErrPct(), equalTo(0.5));
-            assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
-            assertThat(strategy.getGrid().getMaxLevels(),  equalTo(GeoUtils.geoHashLevelsForPrecision(70d)+1));
-        }
-
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                    .startObject("properties").startObject("location")
-                        .field("type", "geo_shape")
-                        .field("tree", "quadtree")
-                        .field("tree_levels", GeoUtils.quadTreeLevelsForPrecision(70d)+1)
-                        .field("precision", "70m")
-                        .field("distance_error_pct", "0.5")
-                    .endObject().endObject()
-                    .endObject().endObject());
-
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-            assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-            LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-            assertThat(strategy.getDistErrPct(), equalTo(0.5));
-            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
-            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(70d)+1));
-        }
-        assertFieldWarnings("tree", "tree_levels", "precision", "distance_error_pct");
-    }
-
-    public void testPointsOnlyOption() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "geohash")
-                .field("points_only", true)
-                .endObject().endObject()
-                .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
-        assertThat(strategy.isPointsOnly(), equalTo(true));
-        assertFieldWarnings("tree", "points_only");
-    }
-
-    public void testLevelDefaults() throws IOException {
-        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                    .startObject("properties").startObject("location")
-                        .field("type", "geo_shape")
-                        .field("tree", "quadtree")
-                        .field("distance_error_pct", "0.5")
-                    .endObject().endObject()
-                    .endObject().endObject());
-
-
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-            assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-            LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-            assertThat(strategy.getDistErrPct(), equalTo(0.5));
-            assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
-            /* 50m is default */
-            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(50d)));
-        }
-
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                    .startObject("properties").startObject("location")
-                        .field("type", "geo_shape")
-                        .field("tree", "geohash")
-                        .field("distance_error_pct", "0.5")
-                    .endObject().endObject()
-                    .endObject().endObject());
-
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-            assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-            LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-            PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-            assertThat(strategy.getDistErrPct(), equalTo(0.5));
-            assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
-            /* 50m is default */
-            assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(50d)));
-        }
-        assertFieldWarnings("tree", "distance_error_pct");
-    }
-
-    public void testGeoShapeMapperMerge() throws Exception {
-        String stage1Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")
-                .startObject("shape").field("type", "geo_shape").field("tree", "geohash")
-                .field("strategy", "recursive")
-                .field("precision", "1m").field("tree_levels", 8).field("distance_error_pct", 0.01)
-                .field("orientation", "ccw")
-                .endObject().endObject().endObject().endObject());
-        MapperService mapperService = createIndex("test").mapperService();
-        DocumentMapper docMapper = mapperService.merge("type", new CompressedXContent(stage1Mapping),
-            MapperService.MergeReason.MAPPING_UPDATE);
-        String stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
-                .startObject("properties").startObject("shape").field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("strategy", "term").field("precision", "1km")
-                .field("tree_levels", 26).field("distance_error_pct", 26)
-                .field("orientation", "cw").endObject().endObject().endObject().endObject());
-        try {
-            mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
-            fail();
-        } catch (IllegalArgumentException e) {
-            assertThat(e.getMessage(), containsString("mapper [shape] has different [strategy]"));
-            assertThat(e.getMessage(), containsString("mapper [shape] has different [tree]"));
-            assertThat(e.getMessage(), containsString("mapper [shape] has different [tree_levels]"));
-            assertThat(e.getMessage(), containsString("mapper [shape] has different [precision]"));
-        }
-
-        // verify nothing changed
-        Mapper fieldMapper = docMapper.mappers().getMapper("shape");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-        assertThat(strategy, instanceOf(RecursivePrefixTreeStrategy.class));
-        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
-        assertThat(strategy.getDistErrPct(), equalTo(0.01));
-        assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(1d)));
-        assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(ShapeBuilder.Orientation.CCW));
-
-        // correct mapping
-        stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
-                .startObject("properties").startObject("shape").field("type", "geo_shape")
-                .field("tree", "geohash")
-                .field("strategy", "recursive")
-                .field("precision", "1m")
-                .field("tree_levels", 8).field("distance_error_pct", 0.001)
-                .field("orientation", "cw").endObject().endObject().endObject().endObject());
-        docMapper = mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
-
-        fieldMapper = docMapper.mappers().getMapper("shape");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-        strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-        assertThat(strategy, instanceOf(RecursivePrefixTreeStrategy.class));
-        assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
-        assertThat(strategy.getDistErrPct(), equalTo(0.001));
-        assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(1d)));
-        assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(ShapeBuilder.Orientation.CW));
-
-        assertFieldWarnings("tree", "strategy", "precision", "tree_levels", "distance_error_pct");
-    }
-
-    public void testEmptyName() throws Exception {
-        // after 5.x
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("")
-            .field("type", "geo_shape")
-            .field("tree", "quadtree")
-            .endObject().endObject()
-            .endObject().endObject());
-        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
-
-        IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
-            () -> parser.parse("type1", new CompressedXContent(mapping))
-        );
-        assertThat(e.getMessage(), containsString("name cannot be empty string"));
-        assertFieldWarnings("tree");
-    }
-
-    public void testSerializeDefaults() throws Exception {
-        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .endObject().endObject()
-                .endObject().endObject());
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            String serialized = toXContentString((LegacyGeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
-            assertTrue(serialized, serialized.contains("\"precision\":\"50.0m\""));
-            assertTrue(serialized, serialized.contains("\"tree_levels\":21"));
-        }
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "geohash")
-                .endObject().endObject()
-                .endObject().endObject());
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            String serialized = toXContentString((LegacyGeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
-            assertTrue(serialized, serialized.contains("\"precision\":\"50.0m\""));
-            assertTrue(serialized, serialized.contains("\"tree_levels\":9"));
-        }
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("tree_levels", "6")
-                .endObject().endObject()
-                .endObject().endObject());
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            String serialized = toXContentString((LegacyGeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
-            assertFalse(serialized, serialized.contains("\"precision\":"));
-            assertTrue(serialized, serialized.contains("\"tree_levels\":6"));
-        }
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("precision", "6")
-                .endObject().endObject()
-                .endObject().endObject());
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            String serialized = toXContentString((LegacyGeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
-            assertTrue(serialized, serialized.contains("\"precision\":\"6.0m\""));
-            assertTrue(serialized, serialized.contains("\"tree_levels\":10"));
-        }
-        {
-            String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("precision", "6m")
-                .field("tree_levels", "5")
-                .endObject().endObject()
-                .endObject().endObject());
-            DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
-            String serialized = toXContentString((LegacyGeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
-            assertTrue(serialized, serialized.contains("\"precision\":\"6.0m\""));
-            assertTrue(serialized, serialized.contains("\"tree_levels\":5"));
-        }
-        assertFieldWarnings("tree", "tree_levels", "precision");
-    }
-
-    public void testPointsOnlyDefaultsWithTermStrategy() throws IOException {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("tree", "quadtree")
-            .field("precision", "10m")
-            .field("strategy", "term")
-            .endObject().endObject()
-            .endObject().endObject());
-
-        DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
-            .parse("type1", new CompressedXContent(mapping));
-        Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
-        assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
-
-        LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
-        PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultPrefixTreeStrategy();
-
-        assertThat(strategy.getDistErrPct(), equalTo(0.0));
-        assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
-        assertThat(strategy.getGrid().getMaxLevels(), equalTo(23));
-        assertThat(strategy.isPointsOnly(), equalTo(true));
-        // term strategy changes the default for points_only, check that we handle it correctly
-        assertThat(toXContentString(geoShapeFieldMapper, false), not(containsString("points_only")));
-        assertFieldWarnings("tree", "precision", "strategy");
-    }
-
-
-    public void testPointsOnlyFalseWithTermStrategy() throws Exception {
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("tree", "quadtree")
-            .field("precision", "10m")
-            .field("strategy", "term")
-            .field("points_only", false)
-            .endObject().endObject()
-            .endObject().endObject());
-
-        DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
-
-        ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class,
-            () -> parser.parse("type1", new CompressedXContent(mapping))
-        );
-        assertThat(e.getMessage(), containsString("points_only cannot be set to false for term strategy"));
-        assertFieldWarnings("tree", "precision", "strategy", "points_only");
-    }
-
-    public String toXContentString(LegacyGeoShapeFieldMapper mapper, boolean includeDefaults) throws IOException {
-        XContentBuilder builder = XContentFactory.jsonBuilder().startObject();
-        ToXContent.Params params;
-        if (includeDefaults) {
-            params = new ToXContent.MapParams(Collections.singletonMap("include_defaults", "true"));
-        } else {
-            params = ToXContent.EMPTY_PARAMS;
-        }
-        mapper.doXContentBody(builder, includeDefaults, params);
-        return Strings.toString(builder.endObject());
-    }
-
-    public String toXContentString(LegacyGeoShapeFieldMapper mapper) throws IOException {
-        return toXContentString(mapper, true);
-    }
-
-}

+ 0 - 86
server/src/test/java/org/elasticsearch/index/mapper/LegacyGeoShapeFieldTypeTests.java

@@ -1,86 +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.mapper;
-
-import org.elasticsearch.common.geo.SpatialStrategy;
-import org.elasticsearch.common.geo.builders.ShapeBuilder;
-import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.GeoShapeFieldType;
-import org.junit.Before;
-
-import java.io.IOException;
-
-public class LegacyGeoShapeFieldTypeTests extends FieldTypeTestCase {
-    @Override
-    protected MappedFieldType createDefaultFieldType() {
-        return new GeoShapeFieldType();
-    }
-
-    @Before
-    public void setupProperties() {
-        addModifier(new Modifier("tree", false) {
-            @Override
-            public void modify(MappedFieldType ft) {
-                ((GeoShapeFieldType)ft).setTree("geohash");
-            }
-        });
-        addModifier(new Modifier("strategy", false) {
-            @Override
-            public void modify(MappedFieldType ft) {
-                ((GeoShapeFieldType)ft).setStrategy(SpatialStrategy.TERM);
-            }
-        });
-        addModifier(new Modifier("tree_levels", false) {
-            @Override
-            public void modify(MappedFieldType ft) {
-                ((GeoShapeFieldType)ft).setTreeLevels(10);
-            }
-        });
-        addModifier(new Modifier("precision", false) {
-            @Override
-            public void modify(MappedFieldType ft) {
-                ((GeoShapeFieldType)ft).setPrecisionInMeters(20);
-            }
-        });
-        addModifier(new Modifier("distance_error_pct", true) {
-            @Override
-            public void modify(MappedFieldType ft) {
-                ((GeoShapeFieldType)ft).setDefaultDistanceErrorPct(0.5);
-            }
-        });
-        addModifier(new Modifier("orientation", true) {
-            @Override
-            public void modify(MappedFieldType ft) {
-                ((GeoShapeFieldType)ft).setOrientation(ShapeBuilder.Orientation.LEFT);
-            }
-        });
-    }
-
-    /**
-     * Test for {@link LegacyGeoShapeFieldMapper.GeoShapeFieldType#setStrategy(SpatialStrategy)} that checks
-     * that {@link LegacyGeoShapeFieldMapper.GeoShapeFieldType#pointsOnly()} gets set as a side effect when using SpatialStrategy.TERM
-     */
-    public void testSetStrategyName() throws IOException {
-        GeoShapeFieldType fieldType = new GeoShapeFieldType();
-        assertFalse(fieldType.pointsOnly());
-        fieldType.setStrategy(SpatialStrategy.RECURSIVE);
-        assertFalse(fieldType.pointsOnly());
-        fieldType.setStrategy(SpatialStrategy.TERM);
-        assertTrue(fieldType.pointsOnly());
-    }
-}

+ 41 - 34
server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java

@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.elasticsearch.index.query;
 
 import org.apache.lucene.search.BooleanQuery;
@@ -28,6 +29,7 @@ import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.SpatialStrategy;
 import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.io.stream.BytesStreamOutput;
@@ -52,41 +54,29 @@ import static org.hamcrest.Matchers.equalTo;
 
 public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQueryBuilder> {
 
-    protected static String indexedShapeId;
-    protected static String indexedShapeType;
-    protected static String indexedShapePath;
-    protected static String indexedShapeIndex;
-    protected static String indexedShapeRouting;
-    protected static ShapeBuilder<?, ?> indexedShapeToReturn;
-
-    @Override
-    protected boolean enableWarningsCheck() {
-        return false;
-    }
-
-    protected String fieldName() {
-        return GEO_SHAPE_FIELD_NAME;
-    }
+    private static String indexedShapeId;
+    private static String indexedShapeType;
+    private static String indexedShapePath;
+    private static String indexedShapeIndex;
+    private static String indexedShapeRouting;
+    private static ShapeBuilder<?, ?> indexedShapeToReturn;
 
     @Override
     protected GeoShapeQueryBuilder doCreateTestQueryBuilder() {
         return doCreateTestQueryBuilder(randomBoolean());
     }
-
-    protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
-        // LatLonShape does not support MultiPoint queries
-        RandomShapeGenerator.ShapeType shapeType =
-            randomFrom(ShapeType.POINT, ShapeType.LINESTRING, ShapeType.MULTILINESTRING, ShapeType.POLYGON);
+    private GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
+        ShapeType shapeType = ShapeType.randomType(random());
         ShapeBuilder<?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
         GeoShapeQueryBuilder builder;
         clearShapeFields();
         if (indexedShape == false) {
-            builder = new GeoShapeQueryBuilder(fieldName(), shape);
+            builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
         } else {
             indexedShapeToReturn = shape;
             indexedShapeId = randomAlphaOfLengthBetween(3, 20);
             indexedShapeType = randomAlphaOfLengthBetween(3, 20);
-            builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId, indexedShapeType);
+            builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeId, indexedShapeType);
             if (randomBoolean()) {
                 indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
                 builder.indexedShapeIndex(indexedShapeIndex);
@@ -101,11 +91,15 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
             }
         }
         if (randomBoolean()) {
-            if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
-                builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
-            } else {
-                // LatLonShape does not support CONTAINS:
-                builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
+            SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
+            // ShapeType.MULTILINESTRING + SpatialStrategy.TERM can lead to large queries and will slow down tests, so
+            // we try to avoid that combination
+            while (shapeType == ShapeType.MULTILINESTRING && strategy == SpatialStrategy.TERM) {
+                strategy = randomFrom(SpatialStrategy.values());
+            }
+            builder.strategy(strategy);
+            if (strategy != SpatialStrategy.TERM) {
+                builder.relation(randomFrom(ShapeRelation.values()));
             }
         }
 
@@ -167,28 +161,41 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
     }
 
     public void testNoShape() throws IOException {
-        expectThrows(IllegalArgumentException.class, () -> new GeoShapeQueryBuilder(fieldName(), null));
+        expectThrows(IllegalArgumentException.class, () -> new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, null));
     }
 
     public void testNoIndexedShape() throws IOException {
         IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
-            () -> new GeoShapeQueryBuilder(fieldName(), null, "type"));
+                () -> new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, null, "type"));
         assertEquals("either shapeBytes or indexedShapeId and indexedShapeType are required", e.getMessage());
     }
 
     public void testNoIndexedShapeType() throws IOException {
         IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
-            () -> new GeoShapeQueryBuilder(fieldName(), "id", null));
+                () -> new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, "id", null));
         assertEquals("indexedShapeType is required if indexedShapeId is specified", e.getMessage());
     }
 
     public void testNoRelation() throws IOException {
         ShapeBuilder<?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null);
-        GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(fieldName(), shape);
+        GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
         IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> builder.relation(null));
         assertEquals("No Shape Relation defined", e.getMessage());
     }
 
+    public void testInvalidRelation() throws IOException {
+        ShapeBuilder<?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null);
+        GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
+        builder.strategy(SpatialStrategy.TERM);
+        expectThrows(IllegalArgumentException.class, () -> builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
+        GeoShapeQueryBuilder builder2 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
+        builder2.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN));
+        expectThrows(IllegalArgumentException.class, () -> builder2.strategy(SpatialStrategy.TERM));
+        GeoShapeQueryBuilder builder3 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
+        builder3.strategy(SpatialStrategy.TERM);
+        expectThrows(IllegalArgumentException.class, () -> builder3.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
+    }
+
     // see #3878
     public void testThatXContentSerializationInsideOfArrayWorks() throws Exception {
         EnvelopeBuilder envelopeBuilder = new EnvelopeBuilder(new Coordinate(0, 0), new Coordinate(10, 10));
@@ -198,7 +205,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
 
     public void testFromJson() throws IOException {
         String json =
-            "{\n" +
+                "{\n" +
                 "  \"geo_shape\" : {\n" +
                 "    \"location\" : {\n" +
                 "      \"shape\" : {\n" +
@@ -223,7 +230,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
         UnsupportedOperationException e = expectThrows(UnsupportedOperationException.class, () -> query.toQuery(createShardContext()));
         assertEquals("query must be rewritten first", e.getMessage());
         QueryBuilder rewrite = rewriteAndFetch(query, createShardContext());
-        GeoShapeQueryBuilder geoShapeQueryBuilder = new GeoShapeQueryBuilder(fieldName(), indexedShapeToReturn);
+        GeoShapeQueryBuilder geoShapeQueryBuilder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeToReturn);
         geoShapeQueryBuilder.strategy(query.strategy());
         geoShapeQueryBuilder.relation(query.relation());
         assertEquals(geoShapeQueryBuilder, rewrite);
@@ -237,7 +244,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
 
         builder = rewriteAndFetch(builder, createShardContext());
 
-        GeoShapeQueryBuilder expectedShape = new GeoShapeQueryBuilder(fieldName(), indexedShapeToReturn);
+        GeoShapeQueryBuilder expectedShape = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeToReturn);
         expectedShape.strategy(shape.strategy());
         expectedShape.relation(shape.relation());
         QueryBuilder expected = new BoolQueryBuilder()

+ 0 - 94
server/src/test/java/org/elasticsearch/index/query/LegacyGeoShapeFieldQueryTests.java

@@ -1,94 +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.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.common.geo.SpatialStrategy;
-import org.elasticsearch.common.geo.builders.ShapeBuilder;
-import org.elasticsearch.test.geo.RandomShapeGenerator;
-import org.elasticsearch.test.geo.RandomShapeGenerator.ShapeType;
-
-import java.io.IOException;
-
-public class LegacyGeoShapeFieldQueryTests extends GeoShapeQueryBuilderTests {
-
-    @Override
-    protected String fieldName() {
-        return LEGACY_GEO_SHAPE_FIELD_NAME;
-    }
-
-    @Override
-    protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
-        ShapeType shapeType = ShapeType.randomType(random());
-        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);
-            indexedShapeType = randomAlphaOfLengthBetween(3, 20);
-            builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId, indexedShapeType);
-            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()) {
-            SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
-            // ShapeType.MULTILINESTRING + SpatialStrategy.TERM can lead to large queries and will slow down tests, so
-            // we try to avoid that combination
-            while (shapeType == ShapeType.MULTILINESTRING && strategy == SpatialStrategy.TERM) {
-                strategy = randomFrom(SpatialStrategy.values());
-            }
-            builder.strategy(strategy);
-            if (strategy != SpatialStrategy.TERM) {
-                builder.relation(randomFrom(ShapeRelation.values()));
-            }
-        }
-
-        if (randomBoolean()) {
-            builder.ignoreUnmapped(randomBoolean());
-        }
-        return builder;
-    }
-
-    public void testInvalidRelation() throws IOException {
-        ShapeBuilder<?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null);
-        GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
-        builder.strategy(SpatialStrategy.TERM);
-        expectThrows(IllegalArgumentException.class, () -> builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
-        GeoShapeQueryBuilder builder2 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
-        builder2.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN));
-        expectThrows(IllegalArgumentException.class, () -> builder2.strategy(SpatialStrategy.TERM));
-        GeoShapeQueryBuilder builder3 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
-        builder3.strategy(SpatialStrategy.TERM);
-        expectThrows(IllegalArgumentException.class, () -> builder3.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
-    }
-}

+ 0 - 1
server/src/test/java/org/elasticsearch/index/query/MatchQueryBuilderTests.java

@@ -62,7 +62,6 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.notNullValue;
 
 public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuilder> {
-
     @Override
     protected MatchQueryBuilder doCreateTestQueryBuilder() {
         String fieldName = randomFrom(STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME, BOOLEAN_FIELD_NAME, INT_FIELD_NAME,

+ 0 - 6
server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java

@@ -1048,12 +1048,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
                         "_field_names", "enabled=true"))),
                 MapperService.MergeReason.MAPPING_UPDATE);
         }
-        assertWarnings(new String[] {
-            "Field parameter [tree_levels] is deprecated and will be removed in a future version.",
-            "Field parameter [precision] is deprecated and will be removed in a future version.",
-            "Field parameter [strategy] is deprecated and will be removed in a future version.",
-            "Field parameter [distance_error_pct] is deprecated and will be removed in a future version."
-        });
     }
 
 

+ 0 - 1
server/src/test/java/org/elasticsearch/search/geo/GeoFilterIT.java

@@ -380,7 +380,6 @@ public class GeoFilterIT extends ESIntegTestCase {
                 .endObject()
                 .startObject("location")
                 .field("type", "geo_shape")
-                .field("ignore_malformed", true)
                 .endObject()
                 .endObject()
                 .endObject()

+ 13 - 12
server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java

@@ -45,21 +45,21 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
     public void testOrientationPersistence() throws Exception {
         String idxName = "orientation";
         String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("shape")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("orientation", "left")
-            .endObject().endObject()
-            .endObject().endObject());
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("orientation", "left")
+                .endObject().endObject()
+                .endObject().endObject());
 
         // create index
         assertAcked(prepareCreate(idxName).addMapping("shape", mapping, XContentType.JSON));
 
         mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("shape")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .field("orientation", "right")
-            .endObject().endObject()
-            .endObject().endObject());
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("orientation", "right")
+                .endObject().endObject()
+                .endObject().endObject());
 
         assertAcked(prepareCreate(idxName+"2").addMapping("shape", mapping, XContentType.JSON));
         ensureGreen(idxName, idxName+"2");
@@ -144,8 +144,9 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
 
         String source = "{\n" +
             "    \"shape\" : {\n" +
-            "        \"type\" : \"bbox\",\n" +
-            "        \"coordinates\" : [[-45.0, 45.0], [45.0, -45.0]]\n" +
+            "        \"type\" : \"circle\",\n" +
+            "        \"coordinates\" : [-45.0, 45.0],\n" +
+            "        \"radius\" : \"100m\"\n" +
             "    }\n" +
             "}";
 

+ 39 - 147
server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java

@@ -19,21 +19,16 @@
 
 package org.elasticsearch.search.geo;
 
-import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
-import org.apache.lucene.geo.GeoTestUtil;
 import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.CheckedSupplier;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.geo.ShapeRelation;
-import org.elasticsearch.common.geo.SpatialStrategy;
 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.MultiPointBuilder;
-import org.elasticsearch.common.geo.builders.PointBuilder;
 import org.elasticsearch.common.geo.builders.PolygonBuilder;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
 import org.elasticsearch.common.settings.Settings;
@@ -41,9 +36,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
-import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.MapperParsingException;
-import org.elasticsearch.index.query.ExistsQueryBuilder;
 import org.elasticsearch.index.query.GeoShapeQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.test.ESSingleNodeTestCase;
@@ -70,26 +63,12 @@ import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.nullValue;
 
 public class GeoShapeQueryTests extends ESSingleNodeTestCase {
-    private static final String[] PREFIX_TREES = new String[] {
-        LegacyGeoShapeFieldMapper.DeprecatedParameters.PrefixTrees.GEOHASH,
-        LegacyGeoShapeFieldMapper.DeprecatedParameters.PrefixTrees.QUADTREE
-    };
-
-    private XContentBuilder createMapping() throws Exception {
-        XContentBuilder xcb = XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape");
-        if (randomBoolean()) {
-            xcb = xcb.field("tree", randomFrom(PREFIX_TREES))
-            .field("strategy", randomFrom(SpatialStrategy.RECURSIVE, SpatialStrategy.TERM));
-        }
-        xcb = xcb.endObject().endObject().endObject().endObject();
-
-        return xcb;
-    }
-
     public void testNullShape() throws Exception {
-        String mapping = Strings.toString(createMapping());
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .endObject().endObject()
+                .endObject().endObject());
         client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
         ensureGreen();
 
@@ -100,7 +79,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
     }
 
     public void testIndexPointsFilterRectangle() throws Exception {
-        String mapping = Strings.toString(createMapping());
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .endObject().endObject()
+                .endObject().endObject());
         client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
         ensureGreen();
 
@@ -142,11 +126,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
     }
 
     public void testEdgeCases() throws Exception {
-        XContentBuilder xcb = XContentFactory.jsonBuilder().startObject().startObject("type1")
-            .startObject("properties").startObject("location")
-            .field("type", "geo_shape")
-            .endObject().endObject().endObject().endObject();
-        String mapping = Strings.toString(xcb);
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .endObject().endObject()
+                .endObject().endObject());
         client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
         ensureGreen();
 
@@ -178,7 +163,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
     }
 
     public void testIndexedShapeReference() throws Exception {
-        String mapping = Strings.toString(createMapping());
+        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
+                .startObject("properties").startObject("location")
+                .field("type", "geo_shape")
+                .field("tree", "quadtree")
+                .endObject().endObject()
+                .endObject().endObject());
         client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
         createIndex("shapes");
         ensureGreen();
@@ -215,7 +205,14 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
     }
 
     public void testIndexedShapeReferenceSourceDisabled() throws Exception {
-        XContentBuilder mapping = createMapping();
+        XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
+                .startObject("properties")
+                    .startObject("location")
+                        .field("type", "geo_shape")
+                        .field("tree", "quadtree")
+                    .endObject()
+                .endObject()
+            .endObject();
         client().admin().indices().prepareCreate("test").addMapping("type1", mapping).get();
         createIndex("shapes", Settings.EMPTY, "shape_type", "_source", "enabled=false");
         ensureGreen();
@@ -329,107 +326,24 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
         assertHitCount(result, 1);
     }
 
-    public void testQueryRandomGeoCollection() throws Exception {
+    public void testShapeFilterWithRandomGeoCollection() throws Exception {
         // Create a random geometry collection.
         GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
-        org.apache.lucene.geo.Polygon randomPoly = GeoTestUtil.nextPolygon();
-        CoordinatesBuilder cb = new CoordinatesBuilder();
-        for (int i = 0; i < randomPoly.numPoints(); ++i) {
-            cb.coordinate(randomPoly.getPolyLon(i), randomPoly.getPolyLat(i));
-        }
-        gcb.shape(new PolygonBuilder(cb));
-
-        logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
-
-        if (randomBoolean()) {
-            client().admin().indices().prepareCreate("test")
-                .addMapping("type", "location", "type=geo_shape").get();
-        } else {
-            client().admin().indices().prepareCreate("test")
-                .addMapping("type", "location", "type=geo_shape,tree=quadtree").get();
-        }
-
-        XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("location"), null).endObject();
-        client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
-
-        ShapeBuilder filterShape = (gcb.getShapeAt(gcb.numShapes() - 1));
-
-        GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("location", filterShape);
-        geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
-        SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get();
-        assertSearchResponse(result);
-        assertHitCount(result, 1);
-    }
-
-    public void testRandomGeoCollectionQuery() throws Exception {
-        boolean usePrefixTrees = randomBoolean();
-        // Create a random geometry collection to index.
-        GeometryCollectionBuilder gcb;
-        if (usePrefixTrees) {
-            gcb = RandomShapeGenerator.createGeometryCollection(random());
-        } else {
-            // vector strategy does not yet support multipoint queries
-            gcb = new GeometryCollectionBuilder();
-            int numShapes = RandomNumbers.randomIntBetween(random(), 1, 4);
-            for (int i = 0; i < numShapes; ++i) {
-                ShapeBuilder shape;
-                do {
-                    shape = RandomShapeGenerator.createShape(random());
-                } while (shape instanceof MultiPointBuilder);
-                gcb.shape(shape);
-            }
-        }
-        org.apache.lucene.geo.Polygon randomPoly = GeoTestUtil.nextPolygon();
-        CoordinatesBuilder cb = new CoordinatesBuilder();
-        for (int i = 0; i < randomPoly.numPoints(); ++i) {
-            cb.coordinate(randomPoly.getPolyLon(i), randomPoly.getPolyLat(i));
-        }
-        gcb.shape(new PolygonBuilder(cb));
 
         logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
 
-        if (usePrefixTrees == false) {
-            client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape")
-                .execute().actionGet();
-        } else {
-            client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
-                .execute().actionGet();
-        }
+        client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
+                .get();
 
         XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("location"), null).endObject();
         client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
 
-        // Create a random geometry collection to query
-        GeometryCollectionBuilder queryCollection = RandomShapeGenerator.createGeometryCollection(random());
-        queryCollection.shape(new PolygonBuilder(cb));
-
-        GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("location", queryCollection);
-        geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
-        SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get();
-        assertSearchResponse(result);
-        assertTrue(result.getHits().getTotalHits().value > 0);
-    }
-
-    /** tests querying a random geometry collection with a point */
-    public void testPointQuery() throws Exception {
-        // Create a random geometry collection to index.
-        GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
-        double[] pt = new double[] {GeoTestUtil.nextLongitude(), GeoTestUtil.nextLatitude()};
-        PointBuilder pb = new PointBuilder(pt[0], pt[1]);
-        gcb.shape(pb);
-        if (randomBoolean()) {
-            client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape")
-                .execute().actionGet();
-        } else {
-            client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
-                .execute().actionGet();
-        }
-        XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("location"), null).endObject();
-        client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
+        ShapeBuilder filterShape = (gcb.getShapeAt(randomIntBetween(0, gcb.numShapes() - 1)));
 
-        GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("location", pb);
-        geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
-        SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get();
+        GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", filterShape);
+        filter.relation(ShapeRelation.INTERSECTS);
+        SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(QueryBuilders.matchAllQuery())
+                .setPostFilter(filter).get();
         assertSearchResponse(result);
         assertHitCount(result, 1);
     }
@@ -461,28 +375,6 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
         assertThat(response.getHits().getTotalHits().value, greaterThan(0L));
     }
 
-    public void testExistsQuery() throws Exception {
-        // Create a random geometry collection.
-        GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
-        logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
-
-        if (randomBoolean()) {
-            client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape")
-                .execute().actionGet();
-        } else {
-            client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
-                .execute().actionGet();
-        }
-
-        XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("location"), null).endObject();
-        client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
-
-        ExistsQueryBuilder eqb = QueryBuilders.existsQuery("location");
-        SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(eqb).get();
-        assertSearchResponse(result);
-        assertHitCount(result, 1);
-    }
-
     public void testShapeFilterWithDefinedGeoCollection() throws Exception {
         createIndex("shapes");
         client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")

+ 0 - 170
server/src/test/java/org/elasticsearch/search/geo/LegacyGeoShapeIntegrationIT.java

@@ -1,170 +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.search.geo;
-
-import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.cluster.ClusterState;
-import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.geo.builders.ShapeBuilder;
-import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.common.xcontent.XContentType;
-import org.elasticsearch.index.IndexService;
-import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
-import org.elasticsearch.index.mapper.MappedFieldType;
-import org.elasticsearch.indices.IndicesService;
-import org.elasticsearch.test.ESIntegTestCase;
-
-import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
-import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.instanceOf;
-
-public class LegacyGeoShapeIntegrationIT extends ESIntegTestCase {
-
-    /**
-     * Test that orientation parameter correctly persists across cluster restart
-     */
-    public void testOrientationPersistence() throws Exception {
-        String idxName = "orientation";
-        String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("shape")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("orientation", "left")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        // create index
-        assertAcked(prepareCreate(idxName).addMapping("shape", mapping, XContentType.JSON));
-
-        mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("shape")
-                .startObject("properties").startObject("location")
-                .field("type", "geo_shape")
-                .field("tree", "quadtree")
-                .field("orientation", "right")
-                .endObject().endObject()
-                .endObject().endObject());
-
-        assertAcked(prepareCreate(idxName+"2").addMapping("shape", mapping, XContentType.JSON));
-        ensureGreen(idxName, idxName+"2");
-
-        internalCluster().fullRestart();
-        ensureGreen(idxName, idxName+"2");
-
-        // left orientation test
-        IndicesService indicesService = internalCluster().getInstance(IndicesService.class, findNodeName(idxName));
-        IndexService indexService = indicesService.indexService(resolveIndex(idxName));
-        MappedFieldType fieldType = indexService.mapperService().fullName("location");
-        assertThat(fieldType, instanceOf(LegacyGeoShapeFieldMapper.GeoShapeFieldType.class));
-
-        LegacyGeoShapeFieldMapper.GeoShapeFieldType gsfm = (LegacyGeoShapeFieldMapper.GeoShapeFieldType)fieldType;
-        ShapeBuilder.Orientation orientation = gsfm.orientation();
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.CLOCKWISE));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.LEFT));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.CW));
-
-        // right orientation test
-        indicesService = internalCluster().getInstance(IndicesService.class, findNodeName(idxName+"2"));
-        indexService = indicesService.indexService(resolveIndex((idxName+"2")));
-        fieldType = indexService.mapperService().fullName("location");
-        assertThat(fieldType, instanceOf(LegacyGeoShapeFieldMapper.GeoShapeFieldType.class));
-
-        gsfm = (LegacyGeoShapeFieldMapper.GeoShapeFieldType)fieldType;
-        orientation = gsfm.orientation();
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.COUNTER_CLOCKWISE));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.RIGHT));
-        assertThat(orientation, equalTo(ShapeBuilder.Orientation.CCW));
-    }
-
-    /**
-     * Test that ignore_malformed on GeoShapeFieldMapper does not fail the entire document
-     */
-    public void testIgnoreMalformed() throws Exception {
-        // create index
-        assertAcked(client().admin().indices().prepareCreate("test")
-            .addMapping("geometry", "shape", "type=geo_shape,tree=quadtree,ignore_malformed=true").get());
-        ensureGreen();
-
-        // test self crossing ccw poly not crossing dateline
-        String polygonGeoJson = Strings.toString(XContentFactory.jsonBuilder().startObject().field("type", "Polygon")
-            .startArray("coordinates")
-            .startArray()
-            .startArray().value(176.0).value(15.0).endArray()
-            .startArray().value(-177.0).value(10.0).endArray()
-            .startArray().value(-177.0).value(-10.0).endArray()
-            .startArray().value(176.0).value(-15.0).endArray()
-            .startArray().value(-177.0).value(15.0).endArray()
-            .startArray().value(172.0).value(0.0).endArray()
-            .startArray().value(176.0).value(15.0).endArray()
-            .endArray()
-            .endArray()
-            .endObject());
-
-        indexRandom(true, client().prepareIndex("test", "geometry", "0").setSource("shape",
-            polygonGeoJson));
-        SearchResponse searchResponse = client().prepareSearch("test").setQuery(matchAllQuery()).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-    }
-
-    /**
-     * Test that the indexed shape routing can be provided if it is required
-     */
-    public void testIndexShapeRouting() throws Exception {
-        String mapping = "{\n" +
-            "    \"_routing\": {\n" +
-            "      \"required\": true\n" +
-            "    },\n" +
-            "    \"properties\": {\n" +
-            "      \"shape\": {\n" +
-            "        \"type\": \"geo_shape\",\n" +
-            "        \"tree\" : \"quadtree\"\n" +
-            "      }\n" +
-            "    }\n" +
-            "  }";
-
-
-        // create index
-        assertAcked(client().admin().indices().prepareCreate("test").addMapping("doc", mapping, XContentType.JSON).get());
-        ensureGreen();
-
-        String source = "{\n" +
-            "    \"shape\" : {\n" +
-            "        \"type\" : \"bbox\",\n" +
-            "        \"coordinates\" : [[-45.0, 45.0], [45.0, -45.0]]\n" +
-            "    }\n" +
-            "}";
-
-        indexRandom(true, client().prepareIndex("test", "doc", "0").setSource(source, XContentType.JSON).setRouting("ABC"));
-
-        SearchResponse searchResponse = client().prepareSearch("test").setQuery(
-            geoShapeQuery("shape", "0", "doc").indexedShapeIndex("test").indexedShapeRouting("ABC")
-        ).get();
-
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-    }
-
-    private String findNodeName(String index) {
-        ClusterState state = client().admin().cluster().prepareState().get().getState();
-        IndexShardRoutingTable shard = state.getRoutingTable().index(index).shard(0);
-        String nodeId = shard.assignedShards().get(0).currentNodeId();
-        return state.getNodes().get(nodeId).getName();
-    }
-}

+ 0 - 2
server/src/test/java/org/elasticsearch/test/geo/RandomShapeGenerator.java

@@ -32,7 +32,6 @@ import org.elasticsearch.common.geo.builders.MultiPointBuilder;
 import org.elasticsearch.common.geo.builders.PointBuilder;
 import org.elasticsearch.common.geo.builders.PolygonBuilder;
 import org.elasticsearch.common.geo.builders.ShapeBuilder;
-import org.elasticsearch.search.geo.GeoShapeQueryTests;
 import org.junit.Assert;
 import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
 import org.locationtech.spatial4j.distance.DistanceUtils;
@@ -154,7 +153,6 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
     /**
      * Creates a random shape useful for randomized testing, NOTE: exercise caution when using this to build random GeometryCollections
      * as creating a large random number of random shapes can result in massive resource consumption
-     * see: {@link GeoShapeQueryTests#testQueryRandomGeoCollection()}
      *
      * The following options are included
      * @param nearPoint Create a shape near a provided point

+ 1 - 19
test/framework/src/main/java/org/elasticsearch/test/AbstractBuilderTestCase.java

@@ -113,7 +113,6 @@ public abstract class AbstractBuilderTestCase extends ESTestCase {
     protected static final String GEO_POINT_FIELD_NAME = "mapped_geo_point";
     protected static final String GEO_POINT_ALIAS_FIELD_NAME = "mapped_geo_point_alias";
     protected static final String GEO_SHAPE_FIELD_NAME = "mapped_geo_shape";
-    protected static final String LEGACY_GEO_SHAPE_FIELD_NAME = "mapped_legacy_geo_shape";
     protected static final String[] MAPPED_FIELD_NAMES = new String[]{STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME,
         INT_FIELD_NAME, INT_RANGE_FIELD_NAME, DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME,
         DATE_RANGE_FIELD_NAME, OBJECT_FIELD_NAME, GEO_POINT_FIELD_NAME, GEO_POINT_ALIAS_FIELD_NAME,
@@ -218,28 +217,12 @@ public abstract class AbstractBuilderTestCase extends ESTestCase {
                         AbstractBuilderTestCase.this, false);
                 return null;
             });
-            if (enableWarningsCheck() == true) {
-                assertDeprecatedGeoWarnings();
-            }
         }
 
         serviceHolder.clientInvocationHandler.delegate = this;
         serviceHolderWithNoType.clientInvocationHandler.delegate = this;
     }
 
-    protected void assertDeprecatedGeoWarnings() {
-        String prefix = "Field parameter [";
-        String postfix = "] is deprecated and will be removed in a future version.";
-        String[] deprecationWarnings = new String[] {
-            prefix + "tree" + postfix,
-            prefix + "tree_levels" + postfix,
-            prefix + "precision" + postfix,
-            prefix + "strategy" + postfix,
-            prefix + "distance_error_pct" + postfix
-        };
-        assertWarnings(deprecationWarnings);
-    }
-
     protected static SearchContext getSearchContext(QueryShardContext context) {
         TestSearchContext testSearchContext = new TestSearchContext(context) {
             @Override
@@ -413,8 +396,7 @@ public abstract class AbstractBuilderTestCase extends ESTestCase {
                     OBJECT_FIELD_NAME, "type=object",
                     GEO_POINT_FIELD_NAME, "type=geo_point",
                     GEO_POINT_ALIAS_FIELD_NAME, "type=alias,path=" + GEO_POINT_FIELD_NAME,
-                    GEO_SHAPE_FIELD_NAME, "type=geo_shape",
-                    LEGACY_GEO_SHAPE_FIELD_NAME, "type=geo_shape,tree=quadtree"
+                    GEO_SHAPE_FIELD_NAME, "type=geo_shape"
                 ))), MapperService.MergeReason.MAPPING_UPDATE);
                 // also add mappings for two inner field in the object field
                 mapperService.merge("_doc", new CompressedXContent("{\"properties\":{\"" + OBJECT_FIELD_NAME + "\":{\"type\":\"object\","