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

This reverts commit 5bc7822562.
This commit is contained in:
Nicholas Knize 2018-12-17 20:09:46 -06:00
parent 2f5300e3a6
commit 96d279ed83
31 changed files with 1227 additions and 2629 deletions

View File

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

View File

@ -52,19 +52,3 @@ as a better alternative.
An error will now be thrown when unknown configuration options are provided An error will now be thrown when unknown configuration options are provided
to similarities. Such unknown parameters were ignored before. 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.

View File

@ -7,7 +7,7 @@ Requires the <<geo-shape,`geo_shape` Mapping>>.
The `geo_shape` query uses the same grid square representation as the The `geo_shape` query uses the same grid square representation as the
`geo_shape` mapping to find documents that have a shape that intersects `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. as defined for the field mapping.
The query supports two ways of defining the query shape, either by 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 * `WITHIN` - Return all documents whose `geo_shape` field
is within the query geometry. is within the query geometry.
* `CONTAINS` - Return all documents whose `geo_shape` field * `CONTAINS` - Return all documents whose `geo_shape` field
contains the query geometry. Note: this is only supported using the contains the query geometry.
`recursive` Prefix Tree Strategy deprecated[6.6]
[float] [float]
==== Ignore Unmapped ==== Ignore Unmapped

View File

@ -19,7 +19,6 @@
package org.elasticsearch.common.geo; 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.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
@ -63,17 +62,6 @@ public enum ShapeRelation implements Writeable {
return null; 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() { public String getRelationName() {
return relationName; return relationName;
} }

View File

@ -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()]); return shapes.toArray(new Object[shapes.size()]);
} }

View File

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

View File

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

View File

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

View File

@ -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;
}
}

View File

@ -18,24 +18,48 @@
*/ */
package org.elasticsearch.index.mapper; package org.elasticsearch.index.mapper;
import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexOptions;
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.IndexableField; 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.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;
import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.settings.Settings; 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.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; 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> * <p>
* Currently Shapes can only be indexed and can only be queried using * Currently Shapes can only be indexed and can only be queried using
* {@link org.elasticsearch.index.query.GeoShapeQueryBuilder}, consequently * {@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] ] * [ [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) { public Builder(String name) {
super (name, new GeoShapeFieldType(), new GeoShapeFieldType()); super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
} }
public Builder(String name, boolean coerce, boolean ignoreMalformed, ShapeBuilder.Orientation orientation, @Override
boolean ignoreZ) { public GeoShapeFieldType fieldType() {
super(name, new GeoShapeFieldType(), new GeoShapeFieldType(), coerce, ignoreMalformed, orientation, ignoreZ); return (GeoShapeFieldType)fieldType;
}
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 @Override
public GeoShapeFieldMapper build(BuilderContext context) { 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); 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 static class TypeParser implements Mapper.TypeParser {
public GeoShapeFieldType() {
super(); @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) { protected GeoShapeFieldType(GeoShapeFieldType ref) {
super(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 @Override
public GeoShapeFieldType clone() { public GeoShapeFieldType clone() {
return new GeoShapeFieldType(this); 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, protected Explicit<Boolean> coerce;
Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce, protected Explicit<Boolean> ignoreMalformed;
Explicit<Boolean> ignoreZValue, Settings indexSettings, 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) { MultiFields multiFields, CopyTo copyTo) {
super(simpleName, fieldType, defaultFieldType, ignoreMalformed, coerce, ignoreZValue, indexSettings, super(simpleName, fieldType, Defaults.FIELD_TYPE, indexSettings, multiFields, copyTo);
multiFields, copyTo); this.coerce = coerce;
this.ignoreMalformed = ignoreMalformed;
this.ignoreZValue = ignoreZValue;
} }
@Override @Override
public GeoShapeFieldType fieldType() { public GeoShapeFieldType fieldType() {
return (GeoShapeFieldType) super.fieldType(); return (GeoShapeFieldType) super.fieldType();
} }
/** parsing logic for {@link LatLonShape} indexing */
@Override @Override
public void parse(ParseContext context) throws IOException { public void parse(ParseContext context) throws IOException {
try { try {
Object shape = context.parseExternalValue(Object.class); Shape shape = context.parseExternalValue(Shape.class);
if (shape == null) { if (shape == null) {
ShapeBuilder shapeBuilder = ShapeParser.parse(context.parser(), this); ShapeBuilder shapeBuilder = ShapeParser.parse(context.parser(), this);
if (shapeBuilder == null) { if (shapeBuilder == null) {
return; 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); indexShape(context, shape);
} catch (Exception e) { } catch (Exception e) {
if (ignoreMalformed.value() == false) { if (ignoreMalformed.value() == false) {
throw new MapperParsingException("failed to parse field [{}] of type [{}]", e, fieldType().name(), throw new MapperParsingException("failed to parse field [{}] of type [{}]", e, fieldType().name(),
fieldType().typeName()); fieldType().typeName());
} }
context.addIgnoredField(fieldType().name()); context.addIgnoredField(fieldType.name());
} }
} }
private void indexShape(ParseContext context, Object luceneShape) { private void indexShape(ParseContext context, Shape shape) {
if (luceneShape instanceof GeoPoint) { List<IndexableField> fields = new ArrayList<>(Arrays.asList(fieldType().defaultStrategy().createIndexableFields(shape)));
GeoPoint pt = (GeoPoint) luceneShape; createFieldNamesField(context, fields);
indexFields(context, LatLonShape.createIndexableFields(name(), pt.lat(), pt.lon())); for (IndexableField field : fields) {
} else if (luceneShape instanceof double[]) { context.doc().add(field);
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)); @Override
} else if (luceneShape instanceof Polygon) { protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
indexFields(context, LatLonShape.createIndexableFields(name(), (Polygon) luceneShape)); }
} else if (luceneShape instanceof double[][]) {
double[][] pts = (double[][])luceneShape; @Override
for (int i = 0; i < pts.length; ++i) { protected void doMerge(Mapper mergeWith) {
indexFields(context, LatLonShape.createIndexableFields(name(), pts[i][1], pts[i][0])); 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;
}
}
@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() + "]");
} }
} else if (luceneShape instanceof Line[]) { }
Line[] lines = (Line[]) luceneShape; if (fieldType().precisionInMeters() != -1) {
for (int i = 0; i < lines.length; ++i) { builder.field(Names.TREE_PRESISION, DistanceUnit.METERS.toString(fieldType().precisionInMeters()));
indexFields(context, LatLonShape.createIndexableFields(name(), lines[i])); } 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));
} else if (luceneShape instanceof Polygon[]) { }
Polygon[] polys = (Polygon[]) luceneShape; if (includeDefaults || fieldType().strategyName().equals(Defaults.STRATEGY) == false) {
for (int i = 0; i < polys.length; ++i) { builder.field(Names.STRATEGY, fieldType().strategyName());
indexFields(context, LatLonShape.createIndexableFields(name(), polys[i])); }
} if (includeDefaults || fieldType().distanceErrorPct() != fieldType().defaultDistanceErrorPct) {
} else if (luceneShape instanceof Rectangle) { builder.field(Names.DISTANCE_ERROR_PCT, fieldType().distanceErrorPct());
// index rectangle as a polygon }
Rectangle r = (Rectangle) luceneShape; if (includeDefaults || fieldType().orientation() != Defaults.ORIENTATION) {
Polygon p = new Polygon(new double[]{r.minLat, r.minLat, r.maxLat, r.maxLat, r.minLat}, builder.field(Names.ORIENTATION, fieldType().orientation());
new double[]{r.minLon, r.maxLon, r.maxLon, r.minLon, r.minLon}); }
indexFields(context, LatLonShape.createIndexableFields(name(), p)); if (fieldType().strategyName().equals(SpatialStrategy.TERM.getStrategyName())) {
} else if (luceneShape instanceof Object[]) { // For TERMs strategy the defaults for points only change to true
// recurse to index geometry collection if (includeDefaults || fieldType().pointsOnly() != true) {
for (Object o : (Object[])luceneShape) { builder.field(Names.STRATEGY_POINTS_ONLY, fieldType().pointsOnly());
indexShape(context, o);
} }
} else { } else {
throw new IllegalArgumentException("invalid shape type found [" + luceneShape.getClass() + "] while indexing shape"); 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());
} }
} }
private void indexFields(ParseContext context, Field[] fields) { public Explicit<Boolean> coerce() {
ArrayList<IndexableField> flist = new ArrayList<>(Arrays.asList(fields)); return coerce;
createFieldNamesField(context, flist); }
for (IndexableField f : flist) {
context.doc().add(f); public Explicit<Boolean> ignoreMalformed() {
} return ignoreMalformed;
}
public Explicit<Boolean> ignoreZValue() {
return ignoreZValue;
}
@Override
protected String contentType() {
return CONTENT_TYPE;
} }
} }

View File

@ -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());
}
}
}
}

View File

@ -19,10 +19,6 @@
package org.elasticsearch.index.query; 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.BooleanClause;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.ConstantScoreQuery;
@ -40,9 +36,8 @@ import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException; 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.ShapeRelation;
import org.elasticsearch.common.geo.ShapesAvailability;
import org.elasticsearch.common.geo.SpatialStrategy; import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.parsers.ShapeParser; 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.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper; import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
import java.io.IOException; import java.io.IOException;
@ -335,9 +329,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
if (relation == null) { if (relation == null) {
throw new IllegalArgumentException("No Shape Relation defined"); 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 [" 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; this.relation = relation;
return this; return this;
@ -382,98 +376,34 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
} else { } else {
throw new QueryShardException(context, "failed to find geo_shape field [" + fieldName + "]"); 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, throw new QueryShardException(context,
"Field [" + fieldName + "] is not of type [geo_shape] but of type [" + fieldType.typeName() + "]"); "Field [" + fieldName + "] is not of type [geo_shape] but of type [" + fieldType.typeName() + "]");
} }
final BaseGeoShapeFieldMapper.BaseGeoShapeFieldType ft = (BaseGeoShapeFieldMapper.BaseGeoShapeFieldType) fieldType; final GeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (GeoShapeFieldMapper.GeoShapeFieldType) fieldType;
PrefixTreeStrategy strategy = shapeFieldType.defaultStrategy();
if (this.strategy != null) {
strategy = shapeFieldType.resolveStrategy(this.strategy);
}
Query query; Query query;
if (strategy != null || ft instanceof LegacyGeoShapeFieldMapper.GeoShapeFieldType) { if (strategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) {
LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (LegacyGeoShapeFieldMapper.GeoShapeFieldType) ft; // this strategy doesn't support disjoint anymore: but it did
SpatialStrategy spatialStrategy = shapeFieldType.strategy(); // before, including creating lucene fieldcache (!)
if (this.strategy != null) { // in this case, execute disjoint as exists && !intersects
spatialStrategy = this.strategy; BooleanQuery.Builder bool = new BooleanQuery.Builder();
} Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
PrefixTreeStrategy prefixTreeStrategy = shapeFieldType.resolvePrefixTreeStrategy(spatialStrategy); Query intersects = strategy.makeQuery(getArgs(shapeToQuery, ShapeRelation.INTERSECTS));
if (prefixTreeStrategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) { bool.add(exists, BooleanClause.Occur.MUST);
// this strategy doesn't support disjoint anymore: but it did bool.add(intersects, BooleanClause.Occur.MUST_NOT);
// before, including creating lucene fieldcache (!) query = new ConstantScoreQuery(bool.build());
// 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 { } else {
query = new ConstantScoreQuery(getVectorQuery(context, shapeToQuery)); query = new ConstantScoreQuery(strategy.makeQuery(getArgs(shapeToQuery, relation)));
} }
return query; return query;
} }
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 + "]");
}
// 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);
} else {
throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape");
}
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();
}
/** /**
* Fetches the Shape with the given ID in the given type and index. * Fetches the Shape with the given ID in the given type and index.
* *
@ -484,6 +414,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
* Shape itself is located * Shape itself is located
*/ */
private void fetch(Client client, GetRequest getRequest, String path, ActionListener<ShapeBuilder> listener) { 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"); getRequest.preference("_local");
client.get(getRequest, new ActionListener<GetResponse>(){ client.get(getRequest, new ActionListener<GetResponse>(){

View File

@ -25,13 +25,13 @@ import org.elasticsearch.action.admin.indices.rollover.MaxDocsCondition;
import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition; import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition;
import org.elasticsearch.action.resync.TransportResyncReplicationAction; import org.elasticsearch.action.resync.TransportResyncReplicationAction;
import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.geo.ShapesAvailability;
import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.engine.EngineFactory; import org.elasticsearch.index.engine.EngineFactory;
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.BinaryFieldMapper; import org.elasticsearch.index.mapper.BinaryFieldMapper;
import org.elasticsearch.index.mapper.BooleanFieldMapper; import org.elasticsearch.index.mapper.BooleanFieldMapper;
import org.elasticsearch.index.mapper.CompletionFieldMapper; 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.FieldAliasMapper;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper; import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.GeoPointFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.IdFieldMapper;
import org.elasticsearch.index.mapper.IgnoredFieldMapper; import org.elasticsearch.index.mapper.IgnoredFieldMapper;
import org.elasticsearch.index.mapper.IndexFieldMapper; 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(CompletionFieldMapper.CONTENT_TYPE, new CompletionFieldMapper.TypeParser());
mappers.put(FieldAliasMapper.CONTENT_TYPE, new FieldAliasMapper.TypeParser()); mappers.put(FieldAliasMapper.CONTENT_TYPE, new FieldAliasMapper.TypeParser());
mappers.put(GeoPointFieldMapper.CONTENT_TYPE, new GeoPointFieldMapper.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 (MapperPlugin mapperPlugin : mapperPlugins) {
for (Map.Entry<String, Mapper.TypeParser> entry : mapperPlugin.getMappers().entrySet()) { for (Map.Entry<String, Mapper.TypeParser> entry : mapperPlugin.getMappers().entrySet()) {

View File

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

View File

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

View File

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

View File

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

View File

@ -18,9 +18,14 @@
*/ */
package org.elasticsearch.index.mapper; 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.Explicit;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; 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.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase { public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
@ -47,10 +53,10 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
public void testDefaultConfiguration() throws IOException { public void testDefaultConfiguration() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1") String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location") .startObject("properties").startObject("location")
.field("type", "geo_shape") .field("type", "geo_shape")
.endObject().endObject() .endObject().endObject()
.endObject().endObject()); .endObject().endObject());
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser() DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
.parse("type1", new CompressedXContent(mapping)); .parse("type1", new CompressedXContent(mapping));
@ -58,8 +64,12 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper; GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
assertThat(geoShapeFieldMapper.fieldType().orientation(), PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
equalTo(GeoShapeFieldMapper.Defaults.ORIENTATION.value()));
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 { public void testOrientationParsing() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1") String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location") .startObject("properties").startObject("location")
.field("type", "geo_shape") .field("type", "geo_shape")
.field("orientation", "left") .field("orientation", "left")
.endObject().endObject() .endObject().endObject()
.endObject().endObject()); .endObject().endObject());
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser() DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
.parse("type1", new CompressedXContent(mapping)); .parse("type1", new CompressedXContent(mapping));
@ -85,11 +95,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
// explicit right orientation test // explicit right orientation test
mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1") mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location") .startObject("properties").startObject("location")
.field("type", "geo_shape") .field("type", "geo_shape")
.field("orientation", "right") .field("orientation", "right")
.endObject().endObject() .endObject().endObject()
.endObject().endObject()); .endObject().endObject());
defaultMapper = createIndex("test2").mapperService().documentMapperParser() defaultMapper = createIndex("test2").mapperService().documentMapperParser()
.parse("type1", new CompressedXContent(mapping)); .parse("type1", new CompressedXContent(mapping));
@ -107,11 +117,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
*/ */
public void testCoerceParsing() throws IOException { public void testCoerceParsing() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1") String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location") .startObject("properties").startObject("location")
.field("type", "geo_shape") .field("type", "geo_shape")
.field("coerce", "true") .field("coerce", "true")
.endObject().endObject() .endObject().endObject()
.endObject().endObject()); .endObject().endObject());
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser() DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
.parse("type1", new CompressedXContent(mapping)); .parse("type1", new CompressedXContent(mapping));
@ -123,11 +133,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
// explicit false coerce test // explicit false coerce test
mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1") mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location") .startObject("properties").startObject("location")
.field("type", "geo_shape") .field("type", "geo_shape")
.field("coerce", "false") .field("coerce", "false")
.endObject().endObject() .endObject().endObject()
.endObject().endObject()); .endObject().endObject());
defaultMapper = createIndex("test2").mapperService().documentMapperParser() defaultMapper = createIndex("test2").mapperService().documentMapperParser()
.parse("type1", new CompressedXContent(mapping)); .parse("type1", new CompressedXContent(mapping));
@ -136,7 +146,6 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
coerce = ((GeoShapeFieldMapper)fieldMapper).coerce().value(); coerce = ((GeoShapeFieldMapper)fieldMapper).coerce().value();
assertThat(coerce, equalTo(false)); assertThat(coerce, equalTo(false));
assertFieldWarnings("tree");
} }
@ -213,45 +222,304 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
assertThat(ignoreMalformed.value(), equalTo(false)); 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());
private void assertFieldWarnings(String... fieldNames) { DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
String[] warnings = new String[fieldNames.length]; .parse("type1", new CompressedXContent(mapping));
for (int i = 0; i < fieldNames.length; ++i) { Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
warnings[i] = "Field parameter [" + fieldNames[i] + "] " assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
+ "is deprecated and will be removed in a future version.";
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());
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 { public void testGeoShapeMapperMerge() throws Exception {
String stage1Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties") String stage1Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("shape").field("type", "geo_shape") .startObject("shape").field("type", "geo_shape").field("tree", "geohash")
.field("orientation", "ccw") .field("strategy", "recursive")
.endObject().endObject().endObject().endObject()); .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(); MapperService mapperService = createIndex("test").mapperService();
DocumentMapper docMapper = mapperService.merge("type", new CompressedXContent(stage1Mapping), DocumentMapper docMapper = mapperService.merge("type", new CompressedXContent(stage1Mapping),
MapperService.MergeReason.MAPPING_UPDATE); MapperService.MergeReason.MAPPING_UPDATE);
String stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") String stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("shape").field("type", "geo_shape") .startObject("properties").startObject("shape").field("type", "geo_shape")
.field("orientation", "cw").endObject().endObject().endObject().endObject()); .field("tree", "quadtree")
mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE); .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 // verify nothing changed
Mapper fieldMapper = docMapper.mappers().getMapper("shape"); Mapper fieldMapper = docMapper.mappers().getMapper("shape");
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper; 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)); assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(ShapeBuilder.Orientation.CCW));
// change mapping; orientation // correct mapping
stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("shape").field("type", "geo_shape") .startObject("properties").startObject("shape").field("type", "geo_shape").field("precision", "1m")
.field("orientation", "cw").endObject().endObject().endObject().endObject()); .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); docMapper = mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
fieldMapper = docMapper.mappers().getMapper("shape"); fieldMapper = docMapper.mappers().getMapper("shape");
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper; 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)); 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") String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location") .startObject("properties").startObject("location")
.field("type", "geo_shape") .field("type", "geo_shape")
.field("tree", "quadtree")
.endObject().endObject() .endObject().endObject()
.endObject().endObject()); .endObject().endObject());
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping)); DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location")); String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
assertTrue(serialized, serialized.contains("\"orientation\":\"" + BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value() + "\"")); 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("\"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 { public String toXContentString(GeoShapeFieldMapper mapper, boolean includeDefaults) throws IOException {

View File

@ -18,23 +18,69 @@
*/ */
package org.elasticsearch.index.mapper; package org.elasticsearch.index.mapper;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper.GeoShapeFieldType; import org.elasticsearch.index.mapper.GeoShapeFieldMapper.GeoShapeFieldType;
import org.junit.Before; import org.junit.Before;
import java.io.IOException;
public class GeoShapeFieldTypeTests extends FieldTypeTestCase { public class GeoShapeFieldTypeTests extends FieldTypeTestCase {
@Override @Override
protected MappedFieldType createDefaultFieldType() { protected MappedFieldType createDefaultFieldType() {
return new GeoShapeFieldType(); return new GeoShapeFieldMapper.GeoShapeFieldType();
} }
@Before @Before
public void setupProperties() { public void setupProperties() {
addModifier(new FieldTypeTestCase.Modifier("orientation", true) { addModifier(new Modifier("tree", false) {
@Override @Override
public void modify(MappedFieldType ft) { public void modify(MappedFieldType ft) {
((GeoShapeFieldType)ft).setOrientation(ShapeBuilder.Orientation.LEFT); ((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) {
((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());
}
} }

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

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

View File

@ -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)));
}
}

View File

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

View File

@ -1048,12 +1048,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
"_field_names", "enabled=true"))), "_field_names", "enabled=true"))),
MapperService.MergeReason.MAPPING_UPDATE); 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."
});
} }

View File

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

View File

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

View File

@ -19,21 +19,16 @@
package org.elasticsearch.search.geo; 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.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.CheckedSupplier; import org.elasticsearch.common.CheckedSupplier;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.geo.ShapeRelation; 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.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder; import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder; import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder; 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.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.settings.Settings; 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.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.index.query.ExistsQueryBuilder;
import org.elasticsearch.index.query.GeoShapeQueryBuilder; import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.ESSingleNodeTestCase;
@ -70,26 +63,12 @@ import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
public class GeoShapeQueryTests extends ESSingleNodeTestCase { 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 { 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(); client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
ensureGreen(); ensureGreen();
@ -100,7 +79,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
} }
public void testIndexPointsFilterRectangle() throws Exception { 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(); client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
ensureGreen(); ensureGreen();
@ -142,11 +126,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
} }
public void testEdgeCases() throws Exception { public void testEdgeCases() throws Exception {
XContentBuilder xcb = XContentFactory.jsonBuilder().startObject().startObject("type1") String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
.startObject("properties").startObject("location") .startObject("properties").startObject("location")
.field("type", "geo_shape") .field("type", "geo_shape")
.endObject().endObject().endObject().endObject(); .field("tree", "quadtree")
String mapping = Strings.toString(xcb); .endObject().endObject()
.endObject().endObject());
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get(); client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
ensureGreen(); ensureGreen();
@ -178,7 +163,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
} }
public void testIndexedShapeReference() throws Exception { 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(); client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
createIndex("shapes"); createIndex("shapes");
ensureGreen(); ensureGreen();
@ -215,7 +205,14 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
} }
public void testIndexedShapeReferenceSourceDisabled() throws Exception { 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(); client().admin().indices().prepareCreate("test").addMapping("type1", mapping).get();
createIndex("shapes", Settings.EMPTY, "shape_type", "_source", "enabled=false"); createIndex("shapes", Settings.EMPTY, "shape_type", "_source", "enabled=false");
ensureGreen(); ensureGreen();
@ -329,107 +326,24 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
assertHitCount(result, 1); assertHitCount(result, 1);
} }
public void testQueryRandomGeoCollection() throws Exception { public void testShapeFilterWithRandomGeoCollection() throws Exception {
// Create a random geometry collection. // Create a random geometry collection.
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random()); 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()); logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
if (randomBoolean()) { client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
client().admin().indices().prepareCreate("test") .get();
.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(); XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("location"), null).endObject();
client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get(); client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
ShapeBuilder filterShape = (gcb.getShapeAt(gcb.numShapes() - 1)); ShapeBuilder filterShape = (gcb.getShapeAt(randomIntBetween(0, gcb.numShapes() - 1)));
GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("location", filterShape); GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", filterShape);
geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS); filter.relation(ShapeRelation.INTERSECTS);
SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get(); SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(QueryBuilders.matchAllQuery())
assertSearchResponse(result); .setPostFilter(filter).get();
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();
}
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();
GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("location", pb);
geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get();
assertSearchResponse(result); assertSearchResponse(result);
assertHitCount(result, 1); assertHitCount(result, 1);
} }
@ -461,28 +375,6 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
assertThat(response.getHits().getTotalHits().value, greaterThan(0L)); 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 { public void testShapeFilterWithDefinedGeoCollection() throws Exception {
createIndex("shapes"); createIndex("shapes");
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree") client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")

View File

@ -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();
}
}

View File

@ -32,7 +32,6 @@ import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder; import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder; import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.search.geo.GeoShapeQueryTests;
import org.junit.Assert; import org.junit.Assert;
import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils; 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 * 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 * as creating a large random number of random shapes can result in massive resource consumption
* see: {@link GeoShapeQueryTests#testQueryRandomGeoCollection()}
* *
* The following options are included * The following options are included
* @param nearPoint Create a shape near a provided point * @param nearPoint Create a shape near a provided point

View File

@ -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_FIELD_NAME = "mapped_geo_point";
protected static final String GEO_POINT_ALIAS_FIELD_NAME = "mapped_geo_point_alias"; 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 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, 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, 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, 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); AbstractBuilderTestCase.this, false);
return null; return null;
}); });
if (enableWarningsCheck() == true) {
assertDeprecatedGeoWarnings();
}
} }
serviceHolder.clientInvocationHandler.delegate = this; serviceHolder.clientInvocationHandler.delegate = this;
serviceHolderWithNoType.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) { protected static SearchContext getSearchContext(QueryShardContext context) {
TestSearchContext testSearchContext = new TestSearchContext(context) { TestSearchContext testSearchContext = new TestSearchContext(context) {
@Override @Override
@ -413,8 +396,7 @@ public abstract class AbstractBuilderTestCase extends ESTestCase {
OBJECT_FIELD_NAME, "type=object", OBJECT_FIELD_NAME, "type=object",
GEO_POINT_FIELD_NAME, "type=geo_point", GEO_POINT_FIELD_NAME, "type=geo_point",
GEO_POINT_ALIAS_FIELD_NAME, "type=alias,path=" + GEO_POINT_FIELD_NAME, GEO_POINT_ALIAS_FIELD_NAME, "type=alias,path=" + GEO_POINT_FIELD_NAME,
GEO_SHAPE_FIELD_NAME, "type=geo_shape", GEO_SHAPE_FIELD_NAME, "type=geo_shape"
LEGACY_GEO_SHAPE_FIELD_NAME, "type=geo_shape,tree=quadtree"
))), MapperService.MergeReason.MAPPING_UPDATE); ))), MapperService.MergeReason.MAPPING_UPDATE);
// also add mappings for two inner field in the object field // also add mappings for two inner field in the object field
mapperService.merge("_doc", new CompressedXContent("{\"properties\":{\"" + OBJECT_FIELD_NAME + "\":{\"type\":\"object\"," mapperService.merge("_doc", new CompressedXContent("{\"properties\":{\"" + OBJECT_FIELD_NAME + "\":{\"type\":\"object\","