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:
parent
2f5300e3a6
commit
96d279ed83
|
@ -21,59 +21,48 @@ type.
|
|||
|=======================================================================
|
||||
|Option |Description| Default
|
||||
|
||||
|`tree |deprecated[6.6, PrefixTrees no longer used] Name of the PrefixTree
|
||||
implementation to be used: `geohash` for GeohashPrefixTree and `quadtree`
|
||||
for QuadPrefixTree. Note: This parameter is only relevant for `term` and
|
||||
`recursive` strategies.
|
||||
| `quadtree`
|
||||
|`tree` |Name of the PrefixTree implementation to be used: `geohash` for
|
||||
GeohashPrefixTree and `quadtree` for QuadPrefixTree.
|
||||
| `geohash`
|
||||
|
||||
|`precision` |deprecated[6.6, PrefixTrees no longer used] This parameter may
|
||||
be used instead of `tree_levels` to set an appropriate value for the
|
||||
`tree_levels` parameter. The value specifies the desired precision and
|
||||
Elasticsearch will calculate the best tree_levels value to honor this
|
||||
precision. The value should be a number followed by an optional distance
|
||||
unit. Valid distance units include: `in`, `inch`, `yd`, `yard`, `mi`,
|
||||
`miles`, `km`, `kilometers`, `m`,`meters`, `cm`,`centimeters`, `mm`,
|
||||
`millimeters`. Note: This parameter is only relevant for `term` and
|
||||
`recursive` strategies.
|
||||
|`precision` |This parameter may be used instead of `tree_levels` to set
|
||||
an appropriate value for the `tree_levels` parameter. The value
|
||||
specifies the desired precision and Elasticsearch will calculate the
|
||||
best tree_levels value to honor this precision. The value should be a
|
||||
number followed by an optional distance unit. Valid distance units
|
||||
include: `in`, `inch`, `yd`, `yard`, `mi`, `miles`, `km`, `kilometers`,
|
||||
`m`,`meters`, `cm`,`centimeters`, `mm`, `millimeters`.
|
||||
| `50m`
|
||||
|
||||
|`tree_levels` |deprecated[6.6, PrefixTrees no longer used] Maximum number
|
||||
of layers to be used by the PrefixTree. This can be used to control the
|
||||
precision of shape representations andtherefore how many terms are
|
||||
indexed. Defaults to the default value of the chosen PrefixTree
|
||||
implementation. Since this parameter requires a certain level of
|
||||
understanding of the underlying implementation, users may use the
|
||||
`precision` parameter instead. However, Elasticsearch only uses the
|
||||
tree_levels parameter internally and this is what is returned via the
|
||||
mapping API even if you use the precision parameter. Note: This parameter
|
||||
is only relevant for `term` and `recursive` strategies.
|
||||
|`tree_levels` |Maximum number of layers to be used by the PrefixTree.
|
||||
This can be used to control the precision of shape representations and
|
||||
therefore how many terms are indexed. Defaults to the default value of
|
||||
the chosen PrefixTree implementation. Since this parameter requires a
|
||||
certain level of understanding of the underlying implementation, users
|
||||
may use the `precision` parameter instead. However, Elasticsearch only
|
||||
uses the tree_levels parameter internally and this is what is returned
|
||||
via the mapping API even if you use the precision parameter.
|
||||
| various
|
||||
|
||||
|`strategy` |deprecated[6.6, PrefixTrees no longer used] The strategy
|
||||
parameter defines the approach for how to represent shapes at indexing
|
||||
and search time. It also influences the capabilities available so it
|
||||
is recommended to let Elasticsearch set this parameter automatically.
|
||||
There are two strategies available: `recursive`, and `term`.
|
||||
Recursive and Term strategies are deprecated and will be removed in a
|
||||
future version. While they are still available, the Term strategy
|
||||
supports point types only (the `points_only` parameter will be
|
||||
automatically set to true) while Recursive strategy supports all
|
||||
shape types. (IMPORTANT: see <<prefix-trees, Prefix trees>> for more
|
||||
detailed information about these strategies)
|
||||
|`strategy` |The strategy parameter defines the approach for how to
|
||||
represent shapes at indexing and search time. It also influences the
|
||||
capabilities available so it is recommended to let Elasticsearch set
|
||||
this parameter automatically. There are two strategies available:
|
||||
`recursive` and `term`. Term strategy supports point types only (the
|
||||
`points_only` parameter will be automatically set to true) while
|
||||
Recursive strategy supports all shape types. (IMPORTANT: see
|
||||
<<prefix-trees, Prefix trees>> for more detailed information)
|
||||
| `recursive`
|
||||
|
||||
|`distance_error_pct` |deprecated[6.6, PrefixTrees no longer used] Used as a
|
||||
hint to the PrefixTree about how precise it should be. Defaults to 0.025 (2.5%)
|
||||
with 0.5 as the maximum supported value. PERFORMANCE NOTE: This value will
|
||||
default to 0 if a `precision` or `tree_level` definition is explicitly defined.
|
||||
This guarantees spatial precision at the level defined in the mapping. This can
|
||||
lead to significant memory usage for high resolution shapes with low error
|
||||
(e.g., large shapes at 1m with < 0.001 error). To improve indexing performance
|
||||
(at the cost of query accuracy) explicitly define `tree_level` or `precision`
|
||||
along with a reasonable `distance_error_pct`, noting that large shapes will have
|
||||
greater false positives. Note: This parameter is only relevant for `term` and
|
||||
`recursive` strategies.
|
||||
|`distance_error_pct` |Used as a hint to the PrefixTree about how
|
||||
precise it should be. Defaults to 0.025 (2.5%) with 0.5 as the maximum
|
||||
supported value. PERFORMANCE NOTE: This value will default to 0 if a `precision` or
|
||||
`tree_level` definition is explicitly defined. This guarantees spatial precision
|
||||
at the level defined in the mapping. This can lead to significant memory usage
|
||||
for high resolution shapes with low error (e.g., large shapes at 1m with < 0.001 error).
|
||||
To improve indexing performance (at the cost of query accuracy) explicitly define
|
||||
`tree_level` or `precision` along with a reasonable `distance_error_pct`, noting
|
||||
that large shapes will have greater false positives.
|
||||
| `0.025`
|
||||
|
||||
|`orientation` |Optionally define how to interpret vertex order for
|
||||
|
@ -88,13 +77,13 @@ sets vertex order for the coordinate list of a geo_shape field but can be
|
|||
overridden in each individual GeoJSON or WKT document.
|
||||
| `ccw`
|
||||
|
||||
|`points_only` |deprecated[6.6, PrefixTrees no longer used] Setting this option to
|
||||
`true` (defaults to `false`) configures the `geo_shape` field type for point
|
||||
shapes only (NOTE: Multi-Points are not yet supported). This optimizes index and
|
||||
search performance for the `geohash` and `quadtree` when it is known that only points
|
||||
will be indexed. At present geo_shape queries can not be executed on `geo_point`
|
||||
field types. This option bridges the gap by improving point performance on a
|
||||
`geo_shape` field so that `geo_shape` queries are optimal on a point only field.
|
||||
|`points_only` |Setting this option to `true` (defaults to `false`) configures
|
||||
the `geo_shape` field type for point shapes only (NOTE: Multi-Points are not
|
||||
yet supported). This optimizes index and search performance for the `geohash` and
|
||||
`quadtree` when it is known that only points will be indexed. At present geo_shape
|
||||
queries can not be executed on `geo_point` field types. This option bridges the gap
|
||||
by improving point performance on a `geo_shape` field so that `geo_shape` queries are
|
||||
optimal on a point only field.
|
||||
| `false`
|
||||
|
||||
|`ignore_malformed` |If true, malformed GeoJSON or WKT shapes are ignored. If
|
||||
|
@ -111,35 +100,16 @@ and reject the whole document.
|
|||
|
||||
|=======================================================================
|
||||
|
||||
|
||||
[[geoshape-indexing-approach]]
|
||||
[float]
|
||||
==== Indexing approach
|
||||
GeoShape types are indexed by decomposing the shape into a triangular mesh and
|
||||
indexing each triangle as a 7 dimension point in a BKD tree. This provides
|
||||
near perfect spatial resolution (down to 1e-7 decimal degree precision) since all
|
||||
spatial relations are computed using an encoded vector representation of the
|
||||
original shape instead of a raster-grid representation as used by the
|
||||
<<prefix-trees>> indexing approach. Performance of the tessellator primarily
|
||||
depends on the number of vertices that define the polygon/multi-polyogn. While
|
||||
this is the default indexing technique prefix trees can still be used by setting
|
||||
the `tree` or `strategy` parameters according to the appropriate
|
||||
<<geo-shape-mapping-options>>. Note that these parameters are now deprecated
|
||||
and will be removed in a future version.
|
||||
|
||||
[[prefix-trees]]
|
||||
[float]
|
||||
==== Prefix trees
|
||||
|
||||
deprecated[6.6, PrefixTrees no longer used] To efficiently represent shapes in
|
||||
an inverted index, Shapes are converted into a series of hashes representing
|
||||
grid squares (commonly referred to as "rasters") using implementations of a
|
||||
PrefixTree. The tree notion comes from the fact that the PrefixTree uses multiple
|
||||
grid layers, each with an increasing level of precision to represent the Earth.
|
||||
This can be thought of as increasing the level of detail of a map or image at higher
|
||||
zoom levels. Since this approach causes precision issues with indexed shape, it has
|
||||
been deprecated in favor of a vector indexing approach that indexes the shapes as a
|
||||
triangular mesh (see <<geoshape-indexing-approach>>).
|
||||
To efficiently represent shapes in the index, Shapes are converted into
|
||||
a series of hashes representing grid squares (commonly referred to as "rasters")
|
||||
using implementations of a PrefixTree. The tree notion comes from the fact that
|
||||
the PrefixTree uses multiple grid layers, each with an increasing level of
|
||||
precision to represent the Earth. This can be thought of as increasing the level
|
||||
of detail of a map or image at higher zoom levels.
|
||||
|
||||
Multiple PrefixTree implementations are provided:
|
||||
|
||||
|
@ -161,10 +131,9 @@ number of levels for the quad trees in Elasticsearch is 29; the default is 21.
|
|||
[[spatial-strategy]]
|
||||
[float]
|
||||
===== Spatial strategies
|
||||
deprecated[6.6, PrefixTrees no longer used] The indexing implementation
|
||||
selected relies on a SpatialStrategy for choosing how to decompose the shapes
|
||||
(either as grid squares or a tessellated triangular mesh). Each strategy
|
||||
answers the following:
|
||||
The PrefixTree implementations rely on a SpatialStrategy for decomposing
|
||||
the provided Shape(s) into approximated grid squares. Each strategy answers
|
||||
the following:
|
||||
|
||||
* What type of Shapes can be indexed?
|
||||
* What types of Query Operations and Shapes can be used?
|
||||
|
@ -177,7 +146,7 @@ are provided:
|
|||
|=======================================================================
|
||||
|Strategy |Supported Shapes |Supported Queries |Multiple Shapes
|
||||
|
||||
|`recursive` |<<input-structure, All>> |`INTERSECTS`, `DISJOINT`, `WITHIN`, `CONTAINS` |Yes
|
||||
|`recursive` |<<input-structure, All>> |`INTERSECTS`, `DISJOINT`, `WITHIN`, `CONTAINS` |Yes
|
||||
|`term` |<<point, Points>> |`INTERSECTS` |Yes
|
||||
|
||||
|=======================================================================
|
||||
|
@ -185,13 +154,13 @@ are provided:
|
|||
[float]
|
||||
===== Accuracy
|
||||
|
||||
`Recursive` and `Term` strategies do not provide 100% accuracy and depending on
|
||||
how they are configured it may return some false positives for `INTERSECTS`,
|
||||
`WITHIN` and `CONTAINS` queries, and some false negatives for `DISJOINT` queries.
|
||||
To mitigate this, it is important to select an appropriate value for the tree_levels
|
||||
parameter and to adjust expectations accordingly. For example, a point may be near
|
||||
the border of a particular grid cell and may thus not match a query that only matches
|
||||
the cell right next to it -- even though the shape is very close to the point.
|
||||
Geo_shape does not provide 100% accuracy and depending on how it is configured
|
||||
it may return some false positives for `INTERSECTS`, `WITHIN` and `CONTAINS`
|
||||
queries, and some false negatives for `DISJOINT` queries. To mitigate this, it
|
||||
is important to select an appropriate value for the tree_levels parameter and
|
||||
to adjust expectations accordingly. For example, a point may be near the border
|
||||
of a particular grid cell and may thus not match a query that only matches the
|
||||
cell right next to it -- even though the shape is very close to the point.
|
||||
|
||||
[float]
|
||||
===== Example
|
||||
|
@ -204,7 +173,9 @@ PUT /example
|
|||
"doc": {
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "geo_shape"
|
||||
"type": "geo_shape",
|
||||
"tree": "quadtree",
|
||||
"precision": "100m"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,23 +185,22 @@ PUT /example
|
|||
// CONSOLE
|
||||
// TESTSETUP
|
||||
|
||||
This mapping definition maps the location field to the geo_shape
|
||||
type using the default vector implementation. It provides
|
||||
approximately 1e-7 decimal degree precision.
|
||||
This mapping maps the location field to the geo_shape type using the
|
||||
quad_tree implementation and a precision of 100m. Elasticsearch translates
|
||||
this into a tree_levels setting of 20.
|
||||
|
||||
[float]
|
||||
===== Performance considerations with Prefix Trees
|
||||
===== Performance considerations
|
||||
|
||||
deprecated[6.6, PrefixTrees no longer used] With prefix trees,
|
||||
Elasticsearch uses the paths in the tree as terms in the inverted index
|
||||
and in queries. The higher the level (and thus the precision), the more
|
||||
terms are generated. Of course, calculating the terms, keeping them in
|
||||
Elasticsearch uses the paths in the prefix tree as terms in the index
|
||||
and in queries. The higher the level is (and thus the precision), the
|
||||
more terms are generated. Of course, calculating the terms, keeping them in
|
||||
memory, and storing them on disk all have a price. Especially with higher
|
||||
tree levels, indices can become extremely large even with a modest amount
|
||||
of data. Additionally, the size of the features also matters. Big, complex
|
||||
polygons can take up a lot of space at higher tree levels. Which setting
|
||||
is right depends on the use case. Generally one trades off accuracy against
|
||||
index size and query performance.
|
||||
tree levels, indices can become extremely large even with a modest
|
||||
amount of data. Additionally, the size of the features also matters.
|
||||
Big, complex polygons can take up a lot of space at higher tree levels.
|
||||
Which setting is right depends on the use case. Generally one trades off
|
||||
accuracy against index size and query performance.
|
||||
|
||||
The defaults in Elasticsearch for both implementations are a compromise
|
||||
between index size and a reasonable level of precision of 50m at the
|
||||
|
@ -628,10 +598,7 @@ POST /example/doc
|
|||
===== Circle
|
||||
|
||||
Elasticsearch supports a `circle` type, which consists of a center
|
||||
point with a radius. Note that this circle representation can only
|
||||
be indexed when using the `recursive` Prefix Tree strategy. For
|
||||
the default <<geoshape-indexing-approach>> circles should be approximated using
|
||||
a `POLYGON`.
|
||||
point with a radius:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
|
@ -645,7 +612,6 @@ POST /example/doc
|
|||
}
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
// TEST[skip:not supported in default]
|
||||
|
||||
Note: The inner `radius` field is required. If not specified, then
|
||||
the units of the `radius` will default to `METERS`.
|
||||
|
|
|
@ -52,19 +52,3 @@ as a better alternative.
|
|||
|
||||
An error will now be thrown when unknown configuration options are provided
|
||||
to similarities. Such unknown parameters were ignored before.
|
||||
|
||||
[float]
|
||||
==== deprecated `geo_shape` Prefix Tree indexing
|
||||
|
||||
`geo_shape` types now default to using a vector indexing approach based on Lucene's new
|
||||
`LatLonShape` field type. This indexes shapes as a triangular mesh instead of decomposing
|
||||
them into individual grid cells. To index using legacy prefix trees `recursive` or `term`
|
||||
strategy must be explicitly defined. Note that these strategies are now deprecated and will
|
||||
be removed in a future version.
|
||||
|
||||
[float]
|
||||
==== deprecated `geo_shape` parameters
|
||||
|
||||
The following type parameters are deprecated for the `geo_shape` field type: `tree`,
|
||||
`precision`, `tree_levels`, `distance_error_pct`, `points_only`, and `strategy`. They
|
||||
will be removed in a future version.
|
|
@ -7,7 +7,7 @@ Requires the <<geo-shape,`geo_shape` Mapping>>.
|
|||
|
||||
The `geo_shape` query uses the same grid square representation as the
|
||||
`geo_shape` mapping to find documents that have a shape that intersects
|
||||
with the query shape. It will also use the same Prefix Tree configuration
|
||||
with the query shape. It will also use the same PrefixTree configuration
|
||||
as defined for the field mapping.
|
||||
|
||||
The query supports two ways of defining the query shape, either by
|
||||
|
@ -157,8 +157,7 @@ has nothing in common with the query geometry.
|
|||
* `WITHIN` - Return all documents whose `geo_shape` field
|
||||
is within the query geometry.
|
||||
* `CONTAINS` - Return all documents whose `geo_shape` field
|
||||
contains the query geometry. Note: this is only supported using the
|
||||
`recursive` Prefix Tree Strategy deprecated[6.6]
|
||||
contains the query geometry.
|
||||
|
||||
[float]
|
||||
==== Ignore Unmapped
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package org.elasticsearch.common.geo;
|
||||
|
||||
import org.apache.lucene.document.LatLonShape.QueryRelation;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
|
@ -63,17 +62,6 @@ public enum ShapeRelation implements Writeable {
|
|||
return null;
|
||||
}
|
||||
|
||||
/** Maps ShapeRelation to Lucene's LatLonShapeRelation */
|
||||
public QueryRelation getLuceneRelation() {
|
||||
switch (this) {
|
||||
case INTERSECTS: return QueryRelation.INTERSECTS;
|
||||
case DISJOINT: return QueryRelation.DISJOINT;
|
||||
case WITHIN: return QueryRelation.WITHIN;
|
||||
default:
|
||||
throw new IllegalArgumentException("ShapeRelation [" + this + "] not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public String getRelationName() {
|
||||
return relationName;
|
||||
}
|
||||
|
|
|
@ -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()]);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,10 @@ import org.elasticsearch.common.geo.GeoShapeType;
|
|||
import org.elasticsearch.common.geo.builders.CircleBuilder;
|
||||
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentSubParser;
|
||||
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -42,22 +41,17 @@ import java.util.List;
|
|||
* complies with geojson specification: https://tools.ietf.org/html/rfc7946
|
||||
*/
|
||||
abstract class GeoJsonParser {
|
||||
protected static ShapeBuilder parse(XContentParser parser, BaseGeoShapeFieldMapper shapeMapper)
|
||||
protected static ShapeBuilder parse(XContentParser parser, GeoShapeFieldMapper shapeMapper)
|
||||
throws IOException {
|
||||
GeoShapeType shapeType = null;
|
||||
DistanceUnit.Distance radius = null;
|
||||
CoordinateNode coordinateNode = null;
|
||||
GeometryCollectionBuilder geometryCollections = null;
|
||||
|
||||
Orientation orientation = (shapeMapper == null)
|
||||
? BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value()
|
||||
: shapeMapper.orientation();
|
||||
Explicit<Boolean> coerce = (shapeMapper == null)
|
||||
? BaseGeoShapeFieldMapper.Defaults.COERCE
|
||||
: shapeMapper.coerce();
|
||||
Explicit<Boolean> ignoreZValue = (shapeMapper == null)
|
||||
? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE
|
||||
: shapeMapper.ignoreZValue();
|
||||
ShapeBuilder.Orientation requestedOrientation =
|
||||
(shapeMapper == null) ? ShapeBuilder.Orientation.RIGHT : shapeMapper.fieldType().orientation();
|
||||
Explicit<Boolean> coerce = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
|
||||
Explicit<Boolean> ignoreZValue = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE : shapeMapper.ignoreZValue();
|
||||
|
||||
String malformedException = null;
|
||||
|
||||
|
@ -108,7 +102,7 @@ abstract class GeoJsonParser {
|
|||
malformedException = "cannot have [" + ShapeParser.FIELD_ORIENTATION + "] with type set to [" + shapeType + "]";
|
||||
}
|
||||
subParser.nextToken();
|
||||
orientation = ShapeBuilder.Orientation.fromString(subParser.text());
|
||||
requestedOrientation = ShapeBuilder.Orientation.fromString(subParser.text());
|
||||
} else {
|
||||
subParser.nextToken();
|
||||
subParser.skipChildren();
|
||||
|
@ -134,7 +128,7 @@ abstract class GeoJsonParser {
|
|||
return geometryCollections;
|
||||
}
|
||||
|
||||
return shapeType.getBuilder(coordinateNode, radius, orientation, coerce.value());
|
||||
return shapeType.getBuilder(coordinateNode, radius, requestedOrientation, coerce.value());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -208,7 +202,7 @@ abstract class GeoJsonParser {
|
|||
* @return Geometry[] geometries of the GeometryCollection
|
||||
* @throws IOException Thrown if an error occurs while reading from the XContentParser
|
||||
*/
|
||||
static GeometryCollectionBuilder parseGeometries(XContentParser parser, BaseGeoShapeFieldMapper mapper) throws
|
||||
static GeometryCollectionBuilder parseGeometries(XContentParser parser, GeoShapeFieldMapper mapper) throws
|
||||
IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
|
||||
throw new ElasticsearchParseException("geometries must be an array of geojson objects");
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.elasticsearch.common.geo.builders.PolygonBuilder;
|
|||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -63,7 +63,7 @@ public class GeoWKTParser {
|
|||
// no instance
|
||||
private GeoWKTParser() {}
|
||||
|
||||
public static ShapeBuilder parse(XContentParser parser, final BaseGeoShapeFieldMapper shapeMapper)
|
||||
public static ShapeBuilder parse(XContentParser parser, final GeoShapeFieldMapper shapeMapper)
|
||||
throws IOException, ElasticsearchParseException {
|
||||
return parseExpectedType(parser, null, shapeMapper);
|
||||
}
|
||||
|
@ -75,12 +75,12 @@ public class GeoWKTParser {
|
|||
|
||||
/** throws an exception if the parsed geometry type does not match the expected shape type */
|
||||
public static ShapeBuilder parseExpectedType(XContentParser parser, final GeoShapeType shapeType,
|
||||
final BaseGeoShapeFieldMapper shapeMapper)
|
||||
final GeoShapeFieldMapper shapeMapper)
|
||||
throws IOException, ElasticsearchParseException {
|
||||
try (StringReader reader = new StringReader(parser.text())) {
|
||||
Explicit<Boolean> ignoreZValue = (shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE :
|
||||
Explicit<Boolean> ignoreZValue = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE :
|
||||
shapeMapper.ignoreZValue();
|
||||
Explicit<Boolean> coerce = (shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
|
||||
Explicit<Boolean> coerce = (shapeMapper == null) ? GeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
|
||||
// setup the tokenizer; configured to read words w/o numbers
|
||||
StreamTokenizer tokenizer = new StreamTokenizer(reader);
|
||||
tokenizer.resetSyntax();
|
||||
|
@ -257,8 +257,7 @@ public class GeoWKTParser {
|
|||
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
|
||||
return null;
|
||||
}
|
||||
PolygonBuilder builder = new PolygonBuilder(parseLinearRing(stream, ignoreZValue, coerce),
|
||||
BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value());
|
||||
PolygonBuilder builder = new PolygonBuilder(parseLinearRing(stream, ignoreZValue, coerce), ShapeBuilder.Orientation.RIGHT);
|
||||
while (nextCloserOrComma(stream).equals(COMMA)) {
|
||||
builder.hole(parseLinearRing(stream, ignoreZValue, coerce));
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContent;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -46,7 +46,7 @@ public interface ShapeParser {
|
|||
* if the parsers current token has been <code>null</code>
|
||||
* @throws IOException if the input could not be read
|
||||
*/
|
||||
static ShapeBuilder parse(XContentParser parser, BaseGeoShapeFieldMapper shapeMapper) throws IOException {
|
||||
static ShapeBuilder parse(XContentParser parser, GeoShapeFieldMapper shapeMapper) throws IOException {
|
||||
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
|
||||
return null;
|
||||
} if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -18,24 +18,48 @@
|
|||
*/
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.LatLonShape;
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.PackedQuadPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.Explicit;
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.geo.GeoUtils;
|
||||
import org.elasticsearch.common.geo.SpatialStrategy;
|
||||
import org.elasticsearch.common.geo.XShapeCollection;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
|
||||
import org.elasticsearch.common.geo.parsers.ShapeParser;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.index.query.QueryShardException;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.jts.JtsGeometry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_MALFORMED;
|
||||
|
||||
/**
|
||||
* FieldMapper for indexing {@link org.apache.lucene.document.LatLonShape}s.
|
||||
* FieldMapper for indexing {@link org.locationtech.spatial4j.shape.Shape}s.
|
||||
* <p>
|
||||
* Currently Shapes can only be indexed and can only be queried using
|
||||
* {@link org.elasticsearch.index.query.GeoShapeQueryBuilder}, consequently
|
||||
|
@ -49,128 +73,554 @@ import java.util.Arrays;
|
|||
* [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
|
||||
* ]
|
||||
* }
|
||||
* <p>
|
||||
* or:
|
||||
* <p>
|
||||
* "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0))
|
||||
*/
|
||||
public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
|
||||
public class GeoShapeFieldMapper extends FieldMapper {
|
||||
|
||||
public static final String CONTENT_TYPE = "geo_shape";
|
||||
|
||||
public static class Names {
|
||||
public static final String TREE = "tree";
|
||||
public static final String TREE_GEOHASH = "geohash";
|
||||
public static final String TREE_QUADTREE = "quadtree";
|
||||
public static final String TREE_LEVELS = "tree_levels";
|
||||
public static final String TREE_PRESISION = "precision";
|
||||
public static final String DISTANCE_ERROR_PCT = "distance_error_pct";
|
||||
public static final String ORIENTATION = "orientation";
|
||||
public static final String STRATEGY = "strategy";
|
||||
public static final String STRATEGY_POINTS_ONLY = "points_only";
|
||||
public static final String COERCE = "coerce";
|
||||
}
|
||||
|
||||
public static class Defaults {
|
||||
public static final String TREE = Names.TREE_GEOHASH;
|
||||
public static final String STRATEGY = SpatialStrategy.RECURSIVE.getStrategyName();
|
||||
public static final boolean POINTS_ONLY = false;
|
||||
public static final int GEOHASH_LEVELS = GeoUtils.geoHashLevelsForPrecision("50m");
|
||||
public static final int QUADTREE_LEVELS = GeoUtils.quadTreeLevelsForPrecision("50m");
|
||||
public static final Orientation ORIENTATION = Orientation.RIGHT;
|
||||
public static final double LEGACY_DISTANCE_ERROR_PCT = 0.025d;
|
||||
public static final Explicit<Boolean> COERCE = new Explicit<>(false, false);
|
||||
public static final Explicit<Boolean> IGNORE_MALFORMED = new Explicit<>(false, false);
|
||||
public static final Explicit<Boolean> IGNORE_Z_VALUE = new Explicit<>(true, false);
|
||||
|
||||
public static final MappedFieldType FIELD_TYPE = new GeoShapeFieldType();
|
||||
|
||||
static {
|
||||
// setting name here is a hack so freeze can be called...instead all these options should be
|
||||
// moved to the default ctor for GeoShapeFieldType, and defaultFieldType() should be removed from mappers...
|
||||
FIELD_TYPE.setName("DoesNotExist");
|
||||
FIELD_TYPE.setIndexOptions(IndexOptions.DOCS);
|
||||
FIELD_TYPE.setTokenized(false);
|
||||
FIELD_TYPE.setStored(false);
|
||||
FIELD_TYPE.setStoreTermVectors(false);
|
||||
FIELD_TYPE.setOmitNorms(true);
|
||||
FIELD_TYPE.freeze();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder extends FieldMapper.Builder<Builder, GeoShapeFieldMapper> {
|
||||
|
||||
private Boolean coerce;
|
||||
private Boolean ignoreMalformed;
|
||||
private Boolean ignoreZValue;
|
||||
|
||||
public static class Builder extends BaseGeoShapeFieldMapper.Builder<BaseGeoShapeFieldMapper.Builder, GeoShapeFieldMapper> {
|
||||
public Builder(String name) {
|
||||
super (name, new GeoShapeFieldType(), new GeoShapeFieldType());
|
||||
super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
|
||||
}
|
||||
|
||||
public Builder(String name, boolean coerce, boolean ignoreMalformed, ShapeBuilder.Orientation orientation,
|
||||
boolean ignoreZ) {
|
||||
super(name, new GeoShapeFieldType(), new GeoShapeFieldType(), coerce, ignoreMalformed, orientation, ignoreZ);
|
||||
@Override
|
||||
public GeoShapeFieldType fieldType() {
|
||||
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
|
||||
public GeoShapeFieldMapper build(BuilderContext context) {
|
||||
GeoShapeFieldType geoShapeFieldType = (GeoShapeFieldType)fieldType;
|
||||
|
||||
if (geoShapeFieldType.treeLevels() == 0 && geoShapeFieldType.precisionInMeters() < 0) {
|
||||
geoShapeFieldType.setDefaultDistanceErrorPct(Defaults.LEGACY_DISTANCE_ERROR_PCT);
|
||||
}
|
||||
setupFieldType(context);
|
||||
return new GeoShapeFieldMapper(name, fieldType, defaultFieldType, ignoreMalformed(context), coerce(context),
|
||||
ignoreZValue(), context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);
|
||||
|
||||
return new GeoShapeFieldMapper(name, fieldType, ignoreMalformed(context), coerce(context), ignoreZValue(context),
|
||||
context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class GeoShapeFieldType extends BaseGeoShapeFieldType {
|
||||
public GeoShapeFieldType() {
|
||||
super();
|
||||
public static class TypeParser implements Mapper.TypeParser {
|
||||
|
||||
@Override
|
||||
public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
|
||||
Builder builder = new Builder(name);
|
||||
Boolean pointsOnly = null;
|
||||
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
|
||||
Map.Entry<String, Object> entry = iterator.next();
|
||||
String fieldName = entry.getKey();
|
||||
Object fieldNode = entry.getValue();
|
||||
if (Names.TREE.equals(fieldName)) {
|
||||
builder.fieldType().setTree(fieldNode.toString());
|
||||
iterator.remove();
|
||||
} else if (Names.TREE_LEVELS.equals(fieldName)) {
|
||||
builder.fieldType().setTreeLevels(Integer.parseInt(fieldNode.toString()));
|
||||
iterator.remove();
|
||||
} else if (Names.TREE_PRESISION.equals(fieldName)) {
|
||||
builder.fieldType().setPrecisionInMeters(DistanceUnit.parse(fieldNode.toString(),
|
||||
DistanceUnit.DEFAULT, DistanceUnit.DEFAULT));
|
||||
iterator.remove();
|
||||
} else if (Names.DISTANCE_ERROR_PCT.equals(fieldName)) {
|
||||
builder.fieldType().setDistanceErrorPct(Double.parseDouble(fieldNode.toString()));
|
||||
iterator.remove();
|
||||
} else if (Names.ORIENTATION.equals(fieldName)) {
|
||||
builder.fieldType().setOrientation(ShapeBuilder.Orientation.fromString(fieldNode.toString()));
|
||||
iterator.remove();
|
||||
} else if (Names.STRATEGY.equals(fieldName)) {
|
||||
builder.fieldType().setStrategyName(fieldNode.toString());
|
||||
iterator.remove();
|
||||
} else if (IGNORE_MALFORMED.equals(fieldName)) {
|
||||
builder.ignoreMalformed(XContentMapValues.nodeBooleanValue(fieldNode, name + ".ignore_malformed"));
|
||||
iterator.remove();
|
||||
} else if (Names.COERCE.equals(fieldName)) {
|
||||
builder.coerce(XContentMapValues.nodeBooleanValue(fieldNode, name + "." + Names.COERCE));
|
||||
iterator.remove();
|
||||
} else if (GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName().equals(fieldName)) {
|
||||
builder.ignoreZValue(XContentMapValues.nodeBooleanValue(fieldNode,
|
||||
name + "." + GeoPointFieldMapper.Names.IGNORE_Z_VALUE.getPreferredName()));
|
||||
iterator.remove();
|
||||
} else if (Names.STRATEGY_POINTS_ONLY.equals(fieldName)) {
|
||||
pointsOnly = XContentMapValues.nodeBooleanValue(fieldNode, name + "." + Names.STRATEGY_POINTS_ONLY);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (pointsOnly != null) {
|
||||
if (builder.fieldType().strategyName.equals(SpatialStrategy.TERM.getStrategyName()) && pointsOnly == false) {
|
||||
throw new IllegalArgumentException("points_only cannot be set to false for term strategy");
|
||||
} else {
|
||||
builder.fieldType().setPointsOnly(pointsOnly);
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class GeoShapeFieldType extends MappedFieldType {
|
||||
|
||||
private String tree = Defaults.TREE;
|
||||
private String strategyName = Defaults.STRATEGY;
|
||||
private boolean pointsOnly = Defaults.POINTS_ONLY;
|
||||
private int treeLevels = 0;
|
||||
private double precisionInMeters = -1;
|
||||
private Double distanceErrorPct;
|
||||
private double defaultDistanceErrorPct = 0.0;
|
||||
private Orientation orientation = Defaults.ORIENTATION;
|
||||
|
||||
// these are built when the field type is frozen
|
||||
private PrefixTreeStrategy defaultStrategy;
|
||||
private RecursivePrefixTreeStrategy recursiveStrategy;
|
||||
private TermQueryPrefixTreeStrategy termStrategy;
|
||||
|
||||
public GeoShapeFieldType() {}
|
||||
|
||||
protected GeoShapeFieldType(GeoShapeFieldType ref) {
|
||||
super(ref);
|
||||
this.tree = ref.tree;
|
||||
this.strategyName = ref.strategyName;
|
||||
this.pointsOnly = ref.pointsOnly;
|
||||
this.treeLevels = ref.treeLevels;
|
||||
this.precisionInMeters = ref.precisionInMeters;
|
||||
this.distanceErrorPct = ref.distanceErrorPct;
|
||||
this.defaultDistanceErrorPct = ref.defaultDistanceErrorPct;
|
||||
this.orientation = ref.orientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoShapeFieldType clone() {
|
||||
return new GeoShapeFieldType(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!super.equals(o)) return false;
|
||||
GeoShapeFieldType that = (GeoShapeFieldType) o;
|
||||
return treeLevels == that.treeLevels &&
|
||||
precisionInMeters == that.precisionInMeters &&
|
||||
defaultDistanceErrorPct == that.defaultDistanceErrorPct &&
|
||||
Objects.equals(tree, that.tree) &&
|
||||
Objects.equals(strategyName, that.strategyName) &&
|
||||
pointsOnly == that.pointsOnly &&
|
||||
Objects.equals(distanceErrorPct, that.distanceErrorPct) &&
|
||||
orientation == that.orientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), tree, strategyName, pointsOnly, treeLevels, precisionInMeters, distanceErrorPct,
|
||||
defaultDistanceErrorPct, orientation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return CONTENT_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeze() {
|
||||
super.freeze();
|
||||
// This is a bit hackish: we need to setup the spatial tree and strategies once the field name is set, which
|
||||
// must be by the time freeze is called.
|
||||
SpatialPrefixTree prefixTree;
|
||||
if ("geohash".equals(tree)) {
|
||||
prefixTree = new GeohashPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
|
||||
getLevels(treeLevels, precisionInMeters, Defaults.GEOHASH_LEVELS, true));
|
||||
} else if ("legacyquadtree".equals(tree)) {
|
||||
prefixTree = new QuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
|
||||
getLevels(treeLevels, precisionInMeters, Defaults.QUADTREE_LEVELS, false));
|
||||
} else if ("quadtree".equals(tree)) {
|
||||
prefixTree = new PackedQuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT,
|
||||
getLevels(treeLevels, precisionInMeters, Defaults.QUADTREE_LEVELS, false));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown prefix tree type [" + tree + "]");
|
||||
}
|
||||
|
||||
recursiveStrategy = new RecursivePrefixTreeStrategy(prefixTree, name());
|
||||
recursiveStrategy.setDistErrPct(distanceErrorPct());
|
||||
recursiveStrategy.setPruneLeafyBranches(false);
|
||||
termStrategy = new TermQueryPrefixTreeStrategy(prefixTree, name());
|
||||
termStrategy.setDistErrPct(distanceErrorPct());
|
||||
defaultStrategy = resolveStrategy(strategyName);
|
||||
defaultStrategy.setPointsOnly(pointsOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCompatibility(MappedFieldType fieldType, List<String> conflicts) {
|
||||
super.checkCompatibility(fieldType, conflicts);
|
||||
GeoShapeFieldType other = (GeoShapeFieldType)fieldType;
|
||||
// prevent user from changing strategies
|
||||
if (strategyName().equals(other.strategyName()) == false) {
|
||||
conflicts.add("mapper [" + name() + "] has different [strategy]");
|
||||
}
|
||||
|
||||
// prevent user from changing trees (changes encoding)
|
||||
if (tree().equals(other.tree()) == false) {
|
||||
conflicts.add("mapper [" + name() + "] has different [tree]");
|
||||
}
|
||||
|
||||
if ((pointsOnly() != other.pointsOnly())) {
|
||||
conflicts.add("mapper [" + name() + "] has different points_only");
|
||||
}
|
||||
|
||||
// TODO we should allow this, but at the moment levels is used to build bookkeeping variables
|
||||
// in lucene's SpatialPrefixTree implementations, need a patch to correct that first
|
||||
if (treeLevels() != other.treeLevels()) {
|
||||
conflicts.add("mapper [" + name() + "] has different [tree_levels]");
|
||||
}
|
||||
if (precisionInMeters() != other.precisionInMeters()) {
|
||||
conflicts.add("mapper [" + name() + "] has different [precision]");
|
||||
}
|
||||
}
|
||||
|
||||
private static int getLevels(int treeLevels, double precisionInMeters, int defaultLevels, boolean geoHash) {
|
||||
if (treeLevels > 0 || precisionInMeters >= 0) {
|
||||
return Math.max(treeLevels, precisionInMeters >= 0 ? (geoHash ? GeoUtils.geoHashLevelsForPrecision(precisionInMeters)
|
||||
: GeoUtils.quadTreeLevelsForPrecision(precisionInMeters)) : 0);
|
||||
}
|
||||
return defaultLevels;
|
||||
}
|
||||
|
||||
public String tree() {
|
||||
return tree;
|
||||
}
|
||||
|
||||
public void setTree(String tree) {
|
||||
checkIfFrozen();
|
||||
this.tree = tree;
|
||||
}
|
||||
|
||||
public String strategyName() {
|
||||
return strategyName;
|
||||
}
|
||||
|
||||
public void setStrategyName(String strategyName) {
|
||||
checkIfFrozen();
|
||||
this.strategyName = strategyName;
|
||||
if (this.strategyName.equals(SpatialStrategy.TERM.getStrategyName())) {
|
||||
this.pointsOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pointsOnly() {
|
||||
return pointsOnly;
|
||||
}
|
||||
|
||||
public void setPointsOnly(boolean pointsOnly) {
|
||||
checkIfFrozen();
|
||||
this.pointsOnly = pointsOnly;
|
||||
}
|
||||
public int treeLevels() {
|
||||
return treeLevels;
|
||||
}
|
||||
|
||||
public void setTreeLevels(int treeLevels) {
|
||||
checkIfFrozen();
|
||||
this.treeLevels = treeLevels;
|
||||
}
|
||||
|
||||
public double precisionInMeters() {
|
||||
return precisionInMeters;
|
||||
}
|
||||
|
||||
public void setPrecisionInMeters(double precisionInMeters) {
|
||||
checkIfFrozen();
|
||||
this.precisionInMeters = precisionInMeters;
|
||||
}
|
||||
|
||||
public double distanceErrorPct() {
|
||||
return distanceErrorPct == null ? defaultDistanceErrorPct : distanceErrorPct;
|
||||
}
|
||||
|
||||
public void setDistanceErrorPct(double distanceErrorPct) {
|
||||
checkIfFrozen();
|
||||
this.distanceErrorPct = distanceErrorPct;
|
||||
}
|
||||
|
||||
public void setDefaultDistanceErrorPct(double defaultDistanceErrorPct) {
|
||||
checkIfFrozen();
|
||||
this.defaultDistanceErrorPct = defaultDistanceErrorPct;
|
||||
}
|
||||
|
||||
public Orientation orientation() { return this.orientation; }
|
||||
|
||||
public void setOrientation(Orientation orientation) {
|
||||
checkIfFrozen();
|
||||
this.orientation = orientation;
|
||||
}
|
||||
|
||||
public PrefixTreeStrategy defaultStrategy() {
|
||||
return this.defaultStrategy;
|
||||
}
|
||||
|
||||
public PrefixTreeStrategy resolveStrategy(SpatialStrategy strategy) {
|
||||
return resolveStrategy(strategy.getStrategyName());
|
||||
}
|
||||
|
||||
public PrefixTreeStrategy resolveStrategy(String strategyName) {
|
||||
if (SpatialStrategy.RECURSIVE.getStrategyName().equals(strategyName)) {
|
||||
return recursiveStrategy;
|
||||
}
|
||||
if (SpatialStrategy.TERM.getStrategyName().equals(strategyName)) {
|
||||
return termStrategy;
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown prefix tree strategy [" + strategyName + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query existsQuery(QueryShardContext context) {
|
||||
return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQuery(Object value, QueryShardContext context) {
|
||||
throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead");
|
||||
}
|
||||
}
|
||||
|
||||
public GeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
|
||||
Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce,
|
||||
Explicit<Boolean> ignoreZValue, Settings indexSettings,
|
||||
protected Explicit<Boolean> coerce;
|
||||
protected Explicit<Boolean> ignoreMalformed;
|
||||
protected Explicit<Boolean> ignoreZValue;
|
||||
|
||||
public GeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, Explicit<Boolean> ignoreMalformed,
|
||||
Explicit<Boolean> coerce, Explicit<Boolean> ignoreZValue, Settings indexSettings,
|
||||
MultiFields multiFields, CopyTo copyTo) {
|
||||
super(simpleName, fieldType, defaultFieldType, ignoreMalformed, coerce, ignoreZValue, indexSettings,
|
||||
multiFields, copyTo);
|
||||
super(simpleName, fieldType, Defaults.FIELD_TYPE, indexSettings, multiFields, copyTo);
|
||||
this.coerce = coerce;
|
||||
this.ignoreMalformed = ignoreMalformed;
|
||||
this.ignoreZValue = ignoreZValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoShapeFieldType fieldType() {
|
||||
return (GeoShapeFieldType) super.fieldType();
|
||||
}
|
||||
|
||||
/** parsing logic for {@link LatLonShape} indexing */
|
||||
@Override
|
||||
public void parse(ParseContext context) throws IOException {
|
||||
try {
|
||||
Object shape = context.parseExternalValue(Object.class);
|
||||
Shape shape = context.parseExternalValue(Shape.class);
|
||||
if (shape == null) {
|
||||
ShapeBuilder shapeBuilder = ShapeParser.parse(context.parser(), this);
|
||||
if (shapeBuilder == null) {
|
||||
return;
|
||||
}
|
||||
shape = shapeBuilder.buildLucene();
|
||||
shape = shapeBuilder.buildS4J();
|
||||
}
|
||||
if (fieldType().pointsOnly() == true) {
|
||||
// index configured for pointsOnly
|
||||
if (shape instanceof XShapeCollection && XShapeCollection.class.cast(shape).pointsOnly()) {
|
||||
// MULTIPOINT data: index each point separately
|
||||
List<Shape> shapes = ((XShapeCollection) shape).getShapes();
|
||||
for (Shape s : shapes) {
|
||||
indexShape(context, s);
|
||||
}
|
||||
return;
|
||||
} else if (shape instanceof Point == false) {
|
||||
throw new MapperParsingException("[{" + fieldType().name() + "}] is configured for points only but a "
|
||||
+ ((shape instanceof JtsGeometry) ? ((JtsGeometry)shape).getGeom().getGeometryType() : shape.getClass())
|
||||
+ " was found");
|
||||
}
|
||||
}
|
||||
indexShape(context, shape);
|
||||
} catch (Exception e) {
|
||||
if (ignoreMalformed.value() == false) {
|
||||
throw new MapperParsingException("failed to parse field [{}] of type [{}]", e, fieldType().name(),
|
||||
fieldType().typeName());
|
||||
fieldType().typeName());
|
||||
}
|
||||
context.addIgnoredField(fieldType().name());
|
||||
context.addIgnoredField(fieldType.name());
|
||||
}
|
||||
}
|
||||
|
||||
private void indexShape(ParseContext context, Object luceneShape) {
|
||||
if (luceneShape instanceof GeoPoint) {
|
||||
GeoPoint pt = (GeoPoint) luceneShape;
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), pt.lat(), pt.lon()));
|
||||
} else if (luceneShape instanceof double[]) {
|
||||
double[] pt = (double[]) luceneShape;
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), pt[1], pt[0]));
|
||||
} else if (luceneShape instanceof Line) {
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), (Line)luceneShape));
|
||||
} else if (luceneShape instanceof Polygon) {
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), (Polygon) luceneShape));
|
||||
} else if (luceneShape instanceof double[][]) {
|
||||
double[][] pts = (double[][])luceneShape;
|
||||
for (int i = 0; i < pts.length; ++i) {
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), pts[i][1], pts[i][0]));
|
||||
private void indexShape(ParseContext context, Shape shape) {
|
||||
List<IndexableField> fields = new ArrayList<>(Arrays.asList(fieldType().defaultStrategy().createIndexableFields(shape)));
|
||||
createFieldNamesField(context, fields);
|
||||
for (IndexableField field : fields) {
|
||||
context.doc().add(field);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doMerge(Mapper mergeWith) {
|
||||
super.doMerge(mergeWith);
|
||||
|
||||
GeoShapeFieldMapper gsfm = (GeoShapeFieldMapper)mergeWith;
|
||||
if (gsfm.coerce.explicit()) {
|
||||
this.coerce = gsfm.coerce;
|
||||
}
|
||||
if (gsfm.ignoreMalformed.explicit()) {
|
||||
this.ignoreMalformed = gsfm.ignoreMalformed;
|
||||
}
|
||||
if (gsfm.ignoreZValue.explicit()) {
|
||||
this.ignoreZValue = gsfm.ignoreZValue;
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), lines[i]));
|
||||
}
|
||||
} else if (luceneShape instanceof Polygon[]) {
|
||||
Polygon[] polys = (Polygon[]) luceneShape;
|
||||
for (int i = 0; i < polys.length; ++i) {
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), polys[i]));
|
||||
}
|
||||
} else if (luceneShape instanceof Rectangle) {
|
||||
// index rectangle as a polygon
|
||||
Rectangle r = (Rectangle) luceneShape;
|
||||
Polygon p = new Polygon(new double[]{r.minLat, r.minLat, r.maxLat, r.maxLat, r.minLat},
|
||||
new double[]{r.minLon, r.maxLon, r.maxLon, r.minLon, r.minLon});
|
||||
indexFields(context, LatLonShape.createIndexableFields(name(), p));
|
||||
} else if (luceneShape instanceof Object[]) {
|
||||
// recurse to index geometry collection
|
||||
for (Object o : (Object[])luceneShape) {
|
||||
indexShape(context, o);
|
||||
}
|
||||
if (fieldType().precisionInMeters() != -1) {
|
||||
builder.field(Names.TREE_PRESISION, DistanceUnit.METERS.toString(fieldType().precisionInMeters()));
|
||||
} else if (includeDefaults && fieldType().treeLevels() == 0) { // defaults only make sense if tree levels are not specified
|
||||
builder.field(Names.TREE_PRESISION, DistanceUnit.METERS.toString(50));
|
||||
}
|
||||
if (includeDefaults || fieldType().strategyName().equals(Defaults.STRATEGY) == false) {
|
||||
builder.field(Names.STRATEGY, fieldType().strategyName());
|
||||
}
|
||||
if (includeDefaults || fieldType().distanceErrorPct() != fieldType().defaultDistanceErrorPct) {
|
||||
builder.field(Names.DISTANCE_ERROR_PCT, fieldType().distanceErrorPct());
|
||||
}
|
||||
if (includeDefaults || fieldType().orientation() != Defaults.ORIENTATION) {
|
||||
builder.field(Names.ORIENTATION, fieldType().orientation());
|
||||
}
|
||||
if (fieldType().strategyName().equals(SpatialStrategy.TERM.getStrategyName())) {
|
||||
// For TERMs strategy the defaults for points only change to true
|
||||
if (includeDefaults || fieldType().pointsOnly() != true) {
|
||||
builder.field(Names.STRATEGY_POINTS_ONLY, fieldType().pointsOnly());
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
ArrayList<IndexableField> flist = new ArrayList<>(Arrays.asList(fields));
|
||||
createFieldNamesField(context, flist);
|
||||
for (IndexableField f : flist) {
|
||||
context.doc().add(f);
|
||||
}
|
||||
public Explicit<Boolean> coerce() {
|
||||
return coerce;
|
||||
}
|
||||
|
||||
public Explicit<Boolean> ignoreMalformed() {
|
||||
return ignoreMalformed;
|
||||
}
|
||||
|
||||
public Explicit<Boolean> ignoreZValue() {
|
||||
return ignoreZValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String contentType() {
|
||||
return CONTENT_TYPE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,10 +19,6 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.document.LatLonShape;
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
|
@ -40,9 +36,8 @@ import org.elasticsearch.action.get.GetResponse;
|
|||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.geo.GeoShapeType;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.geo.ShapesAvailability;
|
||||
import org.elasticsearch.common.geo.SpatialStrategy;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.geo.parsers.ShapeParser;
|
||||
|
@ -53,8 +48,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -335,9 +329,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
|||
if (relation == null) {
|
||||
throw new IllegalArgumentException("No Shape Relation defined");
|
||||
}
|
||||
if (SpatialStrategy.TERM.equals(strategy) && relation != ShapeRelation.INTERSECTS) {
|
||||
if (strategy != null && strategy == SpatialStrategy.TERM && relation != ShapeRelation.INTERSECTS) {
|
||||
throw new IllegalArgumentException("current strategy [" + strategy.getStrategyName() + "] only supports relation ["
|
||||
+ ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]");
|
||||
+ ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]");
|
||||
}
|
||||
this.relation = relation;
|
||||
return this;
|
||||
|
@ -382,98 +376,34 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
|||
} else {
|
||||
throw new QueryShardException(context, "failed to find geo_shape field [" + fieldName + "]");
|
||||
}
|
||||
} else if (fieldType.typeName().equals(BaseGeoShapeFieldMapper.CONTENT_TYPE) == false) {
|
||||
} else if (fieldType.typeName().equals(GeoShapeFieldMapper.CONTENT_TYPE) == false) {
|
||||
throw new QueryShardException(context,
|
||||
"Field [" + fieldName + "] is not of type [geo_shape] but of type [" + fieldType.typeName() + "]");
|
||||
}
|
||||
|
||||
final BaseGeoShapeFieldMapper.BaseGeoShapeFieldType ft = (BaseGeoShapeFieldMapper.BaseGeoShapeFieldType) fieldType;
|
||||
final GeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (GeoShapeFieldMapper.GeoShapeFieldType) fieldType;
|
||||
|
||||
PrefixTreeStrategy strategy = shapeFieldType.defaultStrategy();
|
||||
if (this.strategy != null) {
|
||||
strategy = shapeFieldType.resolveStrategy(this.strategy);
|
||||
}
|
||||
Query query;
|
||||
if (strategy != null || ft instanceof LegacyGeoShapeFieldMapper.GeoShapeFieldType) {
|
||||
LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (LegacyGeoShapeFieldMapper.GeoShapeFieldType) ft;
|
||||
SpatialStrategy spatialStrategy = shapeFieldType.strategy();
|
||||
if (this.strategy != null) {
|
||||
spatialStrategy = this.strategy;
|
||||
}
|
||||
PrefixTreeStrategy prefixTreeStrategy = shapeFieldType.resolvePrefixTreeStrategy(spatialStrategy);
|
||||
if (prefixTreeStrategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) {
|
||||
// this strategy doesn't support disjoint anymore: but it did
|
||||
// before, including creating lucene fieldcache (!)
|
||||
// in this case, execute disjoint as exists && !intersects
|
||||
BooleanQuery.Builder bool = new BooleanQuery.Builder();
|
||||
Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
|
||||
Query intersects = prefixTreeStrategy.makeQuery(getArgs(shapeToQuery, ShapeRelation.INTERSECTS));
|
||||
bool.add(exists, BooleanClause.Occur.MUST);
|
||||
bool.add(intersects, BooleanClause.Occur.MUST_NOT);
|
||||
query = new ConstantScoreQuery(bool.build());
|
||||
} else {
|
||||
query = new ConstantScoreQuery(prefixTreeStrategy.makeQuery(getArgs(shapeToQuery, relation)));
|
||||
}
|
||||
if (strategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) {
|
||||
// this strategy doesn't support disjoint anymore: but it did
|
||||
// before, including creating lucene fieldcache (!)
|
||||
// in this case, execute disjoint as exists && !intersects
|
||||
BooleanQuery.Builder bool = new BooleanQuery.Builder();
|
||||
Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
|
||||
Query intersects = strategy.makeQuery(getArgs(shapeToQuery, ShapeRelation.INTERSECTS));
|
||||
bool.add(exists, BooleanClause.Occur.MUST);
|
||||
bool.add(intersects, BooleanClause.Occur.MUST_NOT);
|
||||
query = new ConstantScoreQuery(bool.build());
|
||||
} else {
|
||||
query = new ConstantScoreQuery(getVectorQuery(context, shapeToQuery));
|
||||
query = new ConstantScoreQuery(strategy.makeQuery(getArgs(shapeToQuery, relation)));
|
||||
}
|
||||
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.
|
||||
*
|
||||
|
@ -484,6 +414,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
|||
* Shape itself is located
|
||||
*/
|
||||
private void fetch(Client client, GetRequest getRequest, String path, ActionListener<ShapeBuilder> listener) {
|
||||
if (ShapesAvailability.JTS_AVAILABLE == false) {
|
||||
throw new IllegalStateException("JTS not available");
|
||||
}
|
||||
getRequest.preference("_local");
|
||||
client.get(getRequest, new ActionListener<GetResponse>(){
|
||||
|
||||
|
|
|
@ -25,13 +25,13 @@ import org.elasticsearch.action.admin.indices.rollover.MaxDocsCondition;
|
|||
import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition;
|
||||
import org.elasticsearch.action.resync.TransportResyncReplicationAction;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.geo.ShapesAvailability;
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
import org.elasticsearch.index.engine.EngineFactory;
|
||||
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.BinaryFieldMapper;
|
||||
import org.elasticsearch.index.mapper.BooleanFieldMapper;
|
||||
import org.elasticsearch.index.mapper.CompletionFieldMapper;
|
||||
|
@ -39,6 +39,7 @@ import org.elasticsearch.index.mapper.DateFieldMapper;
|
|||
import org.elasticsearch.index.mapper.FieldAliasMapper;
|
||||
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
|
||||
import org.elasticsearch.index.mapper.GeoPointFieldMapper;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.IdFieldMapper;
|
||||
import org.elasticsearch.index.mapper.IgnoredFieldMapper;
|
||||
import org.elasticsearch.index.mapper.IndexFieldMapper;
|
||||
|
@ -131,7 +132,10 @@ public class IndicesModule extends AbstractModule {
|
|||
mappers.put(CompletionFieldMapper.CONTENT_TYPE, new CompletionFieldMapper.TypeParser());
|
||||
mappers.put(FieldAliasMapper.CONTENT_TYPE, new FieldAliasMapper.TypeParser());
|
||||
mappers.put(GeoPointFieldMapper.CONTENT_TYPE, new GeoPointFieldMapper.TypeParser());
|
||||
mappers.put(BaseGeoShapeFieldMapper.CONTENT_TYPE, new BaseGeoShapeFieldMapper.TypeParser());
|
||||
|
||||
if (ShapesAvailability.JTS_AVAILABLE && ShapesAvailability.SPATIAL4J_AVAILABLE) {
|
||||
mappers.put(GeoShapeFieldMapper.CONTENT_TYPE, new GeoShapeFieldMapper.TypeParser());
|
||||
}
|
||||
|
||||
for (MapperPlugin mapperPlugin : mapperPlugins) {
|
||||
for (Map.Entry<String, Mapper.TypeParser> entry : mapperPlugin.getMappers().entrySet()) {
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.index.mapper.ContentPath;
|
||||
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.test.VersionUtils;
|
||||
import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions;
|
||||
|
@ -296,8 +296,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
|
||||
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
|
||||
Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
|
||||
final LegacyGeoShapeFieldMapper mapperBuilder =
|
||||
(LegacyGeoShapeFieldMapper) (new LegacyGeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext));
|
||||
final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertEquals(jtsGeom(expected), ShapeParser.parse(parser, mapperBuilder).buildS4J());
|
||||
|
@ -897,6 +896,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.startArray().value(101.0).value(1.0).endArray()
|
||||
.endArray()
|
||||
.endObject();
|
||||
|
||||
ShapeCollection<?> expected = shapeCollection(
|
||||
SPATIAL_CONTEXT.makePoint(100, 0),
|
||||
SPATIAL_CONTEXT.makePoint(101, 1.0));
|
||||
|
@ -968,6 +968,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
shellCoordinates.add(new Coordinate(102, 2));
|
||||
shellCoordinates.add(new Coordinate(102, 3));
|
||||
|
||||
|
||||
shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
|
||||
Polygon withoutHoles = GEOMETRY_FACTORY.createPolygon(shell, null);
|
||||
|
||||
|
@ -1148,6 +1149,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.startObject("nested").startArray("coordinates").value(200.0).value(0.0).endArray().endObject()
|
||||
.startObject("lala").field("type", "NotAPoint").endObject()
|
||||
.endObject();
|
||||
|
||||
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0));
|
||||
assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson, true);
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.ContentPath;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.test.geo.RandomShapeGenerator;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
|
@ -147,6 +146,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
@Override
|
||||
public void testParseLineString() throws IOException {
|
||||
List<Coordinate> coordinates = randomLineStringCoords();
|
||||
|
||||
LineString expected = GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[coordinates.size()]));
|
||||
assertExpected(jtsGeom(expected), new LineStringBuilder(coordinates), true);
|
||||
|
||||
|
@ -279,14 +279,13 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
parser.nextToken();
|
||||
|
||||
Settings indexSettings = Settings.builder()
|
||||
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_7_0_0)
|
||||
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_6_3_0)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
|
||||
|
||||
Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
|
||||
final GeoShapeFieldMapper mapperBuilder =
|
||||
(GeoShapeFieldMapper) (new GeoShapeFieldMapper.Builder("test").ignoreZValue(false).build(mockBuilderContext));
|
||||
final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(false).build(mockBuilderContext);
|
||||
|
||||
// test store z disabled
|
||||
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class,
|
||||
|
@ -324,8 +323,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
|
||||
|
||||
Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
|
||||
final LegacyGeoShapeFieldMapper mapperBuilder =
|
||||
(LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext));
|
||||
final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
|
||||
|
||||
// test store z disabled
|
||||
ElasticsearchException e = expectThrows(ElasticsearchException.class,
|
||||
|
@ -354,8 +352,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
|
||||
|
||||
Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
|
||||
final LegacyGeoShapeFieldMapper mapperBuilder =
|
||||
(LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext));
|
||||
final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
|
||||
|
||||
ShapeBuilder<?, ?> shapeBuilder = ShapeParser.parse(parser, mapperBuilder);
|
||||
assertEquals(shapeBuilder.numDimensions(), 3);
|
||||
|
@ -375,14 +372,12 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
|
||||
|
||||
Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
|
||||
final LegacyGeoShapeFieldMapper defaultMapperBuilder =
|
||||
(LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").coerce(false).build(mockBuilderContext));
|
||||
final GeoShapeFieldMapper defaultMapperBuilder = new GeoShapeFieldMapper.Builder("test").coerce(false).build(mockBuilderContext);
|
||||
ElasticsearchParseException exception = expectThrows(ElasticsearchParseException.class,
|
||||
() -> ShapeParser.parse(parser, defaultMapperBuilder));
|
||||
assertEquals("invalid LinearRing found (coordinates are not closed)", exception.getMessage());
|
||||
|
||||
final LegacyGeoShapeFieldMapper coercingMapperBuilder =
|
||||
(LegacyGeoShapeFieldMapper)(new LegacyGeoShapeFieldMapper.Builder("test").coerce(true).build(mockBuilderContext));
|
||||
final GeoShapeFieldMapper coercingMapperBuilder = new GeoShapeFieldMapper.Builder("test").coerce(true).build(mockBuilderContext);
|
||||
ShapeBuilder<?, ?> shapeBuilder = ShapeParser.parse(parser, coercingMapperBuilder);
|
||||
assertNotNull(shapeBuilder);
|
||||
assertEquals("polygon ((100.0 5.0, 100.0 10.0, 90.0 10.0, 90.0 5.0, 100.0 5.0))", shapeBuilder.toWKT());
|
||||
|
|
|
@ -24,8 +24,8 @@ import org.apache.lucene.index.Term;
|
|||
import org.apache.lucene.search.DocValuesFieldExistsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.geo.builders.PointBuilder;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.elasticsearch.common.collect.Iterators;
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -63,7 +63,6 @@ public class ExternalMapper extends FieldMapper {
|
|||
private BooleanFieldMapper.Builder boolBuilder = new BooleanFieldMapper.Builder(Names.FIELD_BOOL);
|
||||
private GeoPointFieldMapper.Builder latLonPointBuilder = new GeoPointFieldMapper.Builder(Names.FIELD_POINT);
|
||||
private GeoShapeFieldMapper.Builder shapeBuilder = new GeoShapeFieldMapper.Builder(Names.FIELD_SHAPE);
|
||||
private LegacyGeoShapeFieldMapper.Builder legacyShapeBuilder = new LegacyGeoShapeFieldMapper.Builder(Names.FIELD_SHAPE);
|
||||
private Mapper.Builder stringBuilder;
|
||||
private String generatedValue;
|
||||
private String mapperName;
|
||||
|
@ -87,9 +86,7 @@ public class ExternalMapper extends FieldMapper {
|
|||
BinaryFieldMapper binMapper = binBuilder.build(context);
|
||||
BooleanFieldMapper boolMapper = boolBuilder.build(context);
|
||||
GeoPointFieldMapper pointMapper = latLonPointBuilder.build(context);
|
||||
BaseGeoShapeFieldMapper shapeMapper = (context.indexCreatedVersion().before(Version.V_6_6_0))
|
||||
? legacyShapeBuilder.build(context)
|
||||
: shapeBuilder.build(context);
|
||||
GeoShapeFieldMapper shapeMapper = shapeBuilder.build(context);
|
||||
FieldMapper stringMapper = (FieldMapper)stringBuilder.build(context);
|
||||
context.path().remove();
|
||||
|
||||
|
@ -153,13 +150,13 @@ public class ExternalMapper extends FieldMapper {
|
|||
private BinaryFieldMapper binMapper;
|
||||
private BooleanFieldMapper boolMapper;
|
||||
private GeoPointFieldMapper pointMapper;
|
||||
private BaseGeoShapeFieldMapper shapeMapper;
|
||||
private GeoShapeFieldMapper shapeMapper;
|
||||
private FieldMapper stringMapper;
|
||||
|
||||
public ExternalMapper(String simpleName, MappedFieldType fieldType,
|
||||
String generatedValue, String mapperName,
|
||||
BinaryFieldMapper binMapper, BooleanFieldMapper boolMapper, GeoPointFieldMapper pointMapper,
|
||||
BaseGeoShapeFieldMapper shapeMapper, FieldMapper stringMapper, Settings indexSettings,
|
||||
GeoShapeFieldMapper shapeMapper, FieldMapper stringMapper, Settings indexSettings,
|
||||
MultiFields multiFields, CopyTo copyTo) {
|
||||
super(simpleName, fieldType, new ExternalFieldType(), indexSettings, multiFields, copyTo);
|
||||
this.generatedValue = generatedValue;
|
||||
|
@ -185,12 +182,8 @@ public class ExternalMapper extends FieldMapper {
|
|||
pointMapper.parse(context.createExternalValueContext(point));
|
||||
|
||||
// Let's add a Dummy Shape
|
||||
PointBuilder pb = new PointBuilder(-100, 45);
|
||||
if (shapeMapper instanceof GeoShapeFieldMapper) {
|
||||
shapeMapper.parse(context.createExternalValueContext(pb.buildLucene()));
|
||||
} else {
|
||||
shapeMapper.parse(context.createExternalValueContext(pb.buildS4J()));
|
||||
}
|
||||
Point shape = new PointBuilder(-100, 45).buildS4J();
|
||||
shapeMapper.parse(context.createExternalValueContext(shape));
|
||||
|
||||
context = context.createExternalValueContext(generatedValue);
|
||||
|
||||
|
@ -217,7 +210,7 @@ public class ExternalMapper extends FieldMapper {
|
|||
BinaryFieldMapper binMapperUpdate = (BinaryFieldMapper) binMapper.updateFieldType(fullNameToFieldType);
|
||||
BooleanFieldMapper boolMapperUpdate = (BooleanFieldMapper) boolMapper.updateFieldType(fullNameToFieldType);
|
||||
GeoPointFieldMapper pointMapperUpdate = (GeoPointFieldMapper) pointMapper.updateFieldType(fullNameToFieldType);
|
||||
BaseGeoShapeFieldMapper shapeMapperUpdate = (BaseGeoShapeFieldMapper) shapeMapper.updateFieldType(fullNameToFieldType);
|
||||
GeoShapeFieldMapper shapeMapperUpdate = (GeoShapeFieldMapper) shapeMapper.updateFieldType(fullNameToFieldType);
|
||||
TextFieldMapper stringMapperUpdate = (TextFieldMapper) stringMapper.updateFieldType(fullNameToFieldType);
|
||||
if (update == this
|
||||
&& multiFieldsUpdate == multiFields
|
||||
|
|
|
@ -21,13 +21,12 @@ package org.elasticsearch.index.mapper;
|
|||
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
|
||||
import org.elasticsearch.common.geo.builders.PointBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -119,8 +118,7 @@ public class ExternalValuesMapperIntegrationIT extends ESIntegTestCase {
|
|||
assertThat(response.getHits().getTotalHits().value, equalTo((long) 1));
|
||||
|
||||
response = client().prepareSearch("test-idx")
|
||||
.setPostFilter(QueryBuilders.geoShapeQuery("field.shape",
|
||||
new EnvelopeBuilder(new Coordinate(-101, 46), new Coordinate(-99, 44))).relation(ShapeRelation.WITHIN))
|
||||
.setPostFilter(QueryBuilders.geoShapeQuery("field.shape", new PointBuilder(-100, 45)).relation(ShapeRelation.WITHIN))
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(response.getHits().getTotalHits().value, equalTo((long) 1));
|
||||
|
|
|
@ -18,9 +18,14 @@
|
|||
*/
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
|
||||
import org.elasticsearch.common.Explicit;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.geo.GeoUtils;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -37,6 +42,7 @@ import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
||||
|
||||
|
@ -47,10 +53,10 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
|
||||
public void testDefaultConfiguration() throws IOException {
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
|
@ -58,8 +64,12 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
assertThat(geoShapeFieldMapper.fieldType().orientation(),
|
||||
equalTo(GeoShapeFieldMapper.Defaults.ORIENTATION.value()));
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.025d));
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoShapeFieldMapper.Defaults.GEOHASH_LEVELS));
|
||||
assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(GeoShapeFieldMapper.Defaults.ORIENTATION));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,11 +77,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
*/
|
||||
public void testOrientationParsing() throws IOException {
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "left")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "left")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
|
@ -85,11 +95,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
|
||||
// explicit right orientation test
|
||||
mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "right")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "right")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
defaultMapper = createIndex("test2").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
|
@ -107,11 +117,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
*/
|
||||
public void testCoerceParsing() throws IOException {
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("coerce", "true")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("coerce", "true")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
|
@ -123,11 +133,11 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
|
||||
// explicit false coerce test
|
||||
mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("coerce", "false")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("coerce", "false")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
defaultMapper = createIndex("test2").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
|
@ -136,7 +146,6 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
|
||||
coerce = ((GeoShapeFieldMapper)fieldMapper).coerce().value();
|
||||
assertThat(coerce, equalTo(false));
|
||||
assertFieldWarnings("tree");
|
||||
}
|
||||
|
||||
|
||||
|
@ -213,45 +222,304 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
assertThat(ignoreMalformed.value(), equalTo(false));
|
||||
}
|
||||
|
||||
public void testGeohashConfiguration() throws IOException {
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "geohash")
|
||||
.field("tree_levels", "4")
|
||||
.field("distance_error_pct", "0.1")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
private void assertFieldWarnings(String... fieldNames) {
|
||||
String[] warnings = new String[fieldNames.length];
|
||||
for (int i = 0; i < fieldNames.length; ++i) {
|
||||
warnings[i] = "Field parameter [" + fieldNames[i] + "] "
|
||||
+ "is deprecated and will be removed in a future version.";
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.1));
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(4));
|
||||
}
|
||||
|
||||
public void testQuadtreeConfiguration() throws IOException {
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.field("tree_levels", "6")
|
||||
.field("distance_error_pct", "0.5")
|
||||
.field("points_only", true)
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.5));
|
||||
assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(6));
|
||||
assertThat(strategy.isPointsOnly(), equalTo(true));
|
||||
}
|
||||
|
||||
public void testLevelPrecisionConfiguration() throws IOException {
|
||||
DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
|
||||
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.field("tree_levels", "6")
|
||||
.field("precision", "70m")
|
||||
.field("distance_error_pct", "0.5")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.5));
|
||||
assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
|
||||
// 70m is more precise so it wins
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(70d)));
|
||||
}
|
||||
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.field("tree_levels", "26")
|
||||
.field("precision", "70m")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
// distance_error_pct was not specified so we expect the mapper to take the highest precision between "precision" and
|
||||
// "tree_levels" setting distErrPct to 0 to guarantee desired precision
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.0));
|
||||
assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
|
||||
// 70m is less precise so it loses
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(26));
|
||||
}
|
||||
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "geohash")
|
||||
.field("tree_levels", "6")
|
||||
.field("precision", "70m")
|
||||
.field("distance_error_pct", "0.5")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.5));
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
// 70m is more precise so it wins
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(70d)));
|
||||
}
|
||||
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "geohash")
|
||||
.field("tree_levels", GeoUtils.geoHashLevelsForPrecision(70d)+1)
|
||||
.field("precision", "70m")
|
||||
.field("distance_error_pct", "0.5")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.5));
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(70d)+1));
|
||||
}
|
||||
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.field("tree_levels", GeoUtils.quadTreeLevelsForPrecision(70d)+1)
|
||||
.field("precision", "70m")
|
||||
.field("distance_error_pct", "0.5")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.5));
|
||||
assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(70d)+1));
|
||||
}
|
||||
}
|
||||
|
||||
public void testPointsOnlyOption() throws IOException {
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "geohash")
|
||||
.field("points_only", true)
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser()
|
||||
.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
assertThat(strategy.isPointsOnly(), equalTo(true));
|
||||
}
|
||||
|
||||
public void testLevelDefaults() throws IOException {
|
||||
DocumentMapperParser parser = createIndex("test").mapperService().documentMapperParser();
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.field("distance_error_pct", "0.5")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.5));
|
||||
assertThat(strategy.getGrid(), instanceOf(QuadPrefixTree.class));
|
||||
/* 50m is default */
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.quadTreeLevelsForPrecision(50d)));
|
||||
}
|
||||
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "geohash")
|
||||
.field("distance_error_pct", "0.5")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
Mapper fieldMapper = defaultMapper.mappers().getMapper("location");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.5));
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
/* 50m is default */
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(50d)));
|
||||
}
|
||||
}
|
||||
|
||||
public void testGeoShapeMapperMerge() throws Exception {
|
||||
String stage1Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")
|
||||
.startObject("shape").field("type", "geo_shape")
|
||||
.field("orientation", "ccw")
|
||||
.endObject().endObject().endObject().endObject());
|
||||
.startObject("shape").field("type", "geo_shape").field("tree", "geohash")
|
||||
.field("strategy", "recursive")
|
||||
.field("precision", "1m").field("tree_levels", 8).field("distance_error_pct", 0.01)
|
||||
.field("orientation", "ccw")
|
||||
.endObject().endObject().endObject().endObject());
|
||||
MapperService mapperService = createIndex("test").mapperService();
|
||||
DocumentMapper docMapper = mapperService.merge("type", new CompressedXContent(stage1Mapping),
|
||||
MapperService.MergeReason.MAPPING_UPDATE);
|
||||
String stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.startObject("properties").startObject("shape").field("type", "geo_shape")
|
||||
.field("orientation", "cw").endObject().endObject().endObject().endObject());
|
||||
mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
|
||||
.startObject("properties").startObject("shape").field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.field("strategy", "term").field("precision", "1km")
|
||||
.field("tree_levels", 26).field("distance_error_pct", 26)
|
||||
.field("orientation", "cw").endObject().endObject().endObject().endObject());
|
||||
try {
|
||||
mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), containsString("mapper [shape] has different [strategy]"));
|
||||
assertThat(e.getMessage(), containsString("mapper [shape] has different [tree]"));
|
||||
assertThat(e.getMessage(), containsString("mapper [shape] has different [tree_levels]"));
|
||||
assertThat(e.getMessage(), containsString("mapper [shape] has different [precision]"));
|
||||
}
|
||||
|
||||
// verify nothing changed
|
||||
Mapper fieldMapper = docMapper.mappers().getMapper("shape");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
GeoShapeFieldMapper geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
PrefixTreeStrategy strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy, instanceOf(RecursivePrefixTreeStrategy.class));
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.01));
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(1d)));
|
||||
assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(ShapeBuilder.Orientation.CCW));
|
||||
|
||||
// change mapping; orientation
|
||||
// correct mapping
|
||||
stage2Mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.startObject("properties").startObject("shape").field("type", "geo_shape")
|
||||
.field("orientation", "cw").endObject().endObject().endObject().endObject());
|
||||
.startObject("properties").startObject("shape").field("type", "geo_shape").field("precision", "1m")
|
||||
.field("tree_levels", 8).field("distance_error_pct", 0.001)
|
||||
.field("orientation", "cw").endObject().endObject().endObject().endObject());
|
||||
docMapper = mapperService.merge("type", new CompressedXContent(stage2Mapping), MapperService.MergeReason.MAPPING_UPDATE);
|
||||
|
||||
fieldMapper = docMapper.mappers().getMapper("shape");
|
||||
assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));
|
||||
|
||||
geoShapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
|
||||
strategy = geoShapeFieldMapper.fieldType().defaultStrategy();
|
||||
|
||||
assertThat(strategy, instanceOf(RecursivePrefixTreeStrategy.class));
|
||||
assertThat(strategy.getGrid(), instanceOf(GeohashPrefixTree.class));
|
||||
assertThat(strategy.getDistErrPct(), equalTo(0.001));
|
||||
assertThat(strategy.getGrid().getMaxLevels(), equalTo(GeoUtils.geoHashLevelsForPrecision(1d)));
|
||||
assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(ShapeBuilder.Orientation.CW));
|
||||
}
|
||||
|
||||
|
@ -276,12 +544,112 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
|
|||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
|
||||
String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
|
||||
assertTrue(serialized, serialized.contains("\"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 {
|
||||
|
|
|
@ -18,23 +18,69 @@
|
|||
*/
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.elasticsearch.common.geo.SpatialStrategy;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper.GeoShapeFieldType;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GeoShapeFieldTypeTests extends FieldTypeTestCase {
|
||||
@Override
|
||||
protected MappedFieldType createDefaultFieldType() {
|
||||
return new GeoShapeFieldType();
|
||||
return new GeoShapeFieldMapper.GeoShapeFieldType();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupProperties() {
|
||||
addModifier(new FieldTypeTestCase.Modifier("orientation", true) {
|
||||
addModifier(new Modifier("tree", false) {
|
||||
@Override
|
||||
public void modify(MappedFieldType ft) {
|
||||
((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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
|
@ -28,6 +29,7 @@ import org.elasticsearch.action.get.GetResponse;
|
|||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.geo.SpatialStrategy;
|
||||
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
|
@ -52,41 +54,29 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
|
||||
public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQueryBuilder> {
|
||||
|
||||
protected static String indexedShapeId;
|
||||
protected static String indexedShapeType;
|
||||
protected static String indexedShapePath;
|
||||
protected static String indexedShapeIndex;
|
||||
protected static String indexedShapeRouting;
|
||||
protected static ShapeBuilder<?, ?> indexedShapeToReturn;
|
||||
|
||||
@Override
|
||||
protected boolean enableWarningsCheck() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String fieldName() {
|
||||
return GEO_SHAPE_FIELD_NAME;
|
||||
}
|
||||
private static String indexedShapeId;
|
||||
private static String indexedShapeType;
|
||||
private static String indexedShapePath;
|
||||
private static String indexedShapeIndex;
|
||||
private static String indexedShapeRouting;
|
||||
private static ShapeBuilder<?, ?> indexedShapeToReturn;
|
||||
|
||||
@Override
|
||||
protected GeoShapeQueryBuilder doCreateTestQueryBuilder() {
|
||||
return doCreateTestQueryBuilder(randomBoolean());
|
||||
}
|
||||
|
||||
protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
|
||||
// LatLonShape does not support MultiPoint queries
|
||||
RandomShapeGenerator.ShapeType shapeType =
|
||||
randomFrom(ShapeType.POINT, ShapeType.LINESTRING, ShapeType.MULTILINESTRING, ShapeType.POLYGON);
|
||||
private GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
|
||||
ShapeType shapeType = ShapeType.randomType(random());
|
||||
ShapeBuilder<?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType);
|
||||
GeoShapeQueryBuilder builder;
|
||||
clearShapeFields();
|
||||
if (indexedShape == false) {
|
||||
builder = new GeoShapeQueryBuilder(fieldName(), shape);
|
||||
builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
|
||||
} else {
|
||||
indexedShapeToReturn = shape;
|
||||
indexedShapeId = randomAlphaOfLengthBetween(3, 20);
|
||||
indexedShapeType = randomAlphaOfLengthBetween(3, 20);
|
||||
builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId, indexedShapeType);
|
||||
builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeId, indexedShapeType);
|
||||
if (randomBoolean()) {
|
||||
indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
|
||||
builder.indexedShapeIndex(indexedShapeIndex);
|
||||
|
@ -101,11 +91,15 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
|||
}
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) {
|
||||
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS));
|
||||
} else {
|
||||
// LatLonShape does not support CONTAINS:
|
||||
builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.WITHIN));
|
||||
SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
|
||||
// ShapeType.MULTILINESTRING + SpatialStrategy.TERM can lead to large queries and will slow down tests, so
|
||||
// we try to avoid that combination
|
||||
while (shapeType == ShapeType.MULTILINESTRING && strategy == SpatialStrategy.TERM) {
|
||||
strategy = randomFrom(SpatialStrategy.values());
|
||||
}
|
||||
builder.strategy(strategy);
|
||||
if (strategy != SpatialStrategy.TERM) {
|
||||
builder.relation(randomFrom(ShapeRelation.values()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,28 +161,41 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
|||
}
|
||||
|
||||
public void testNoShape() throws IOException {
|
||||
expectThrows(IllegalArgumentException.class, () -> new GeoShapeQueryBuilder(fieldName(), null));
|
||||
expectThrows(IllegalArgumentException.class, () -> new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, null));
|
||||
}
|
||||
|
||||
public void testNoIndexedShape() throws IOException {
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new GeoShapeQueryBuilder(fieldName(), null, "type"));
|
||||
() -> new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, null, "type"));
|
||||
assertEquals("either shapeBytes or indexedShapeId and indexedShapeType are required", e.getMessage());
|
||||
}
|
||||
|
||||
public void testNoIndexedShapeType() throws IOException {
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new GeoShapeQueryBuilder(fieldName(), "id", null));
|
||||
() -> new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, "id", null));
|
||||
assertEquals("indexedShapeType is required if indexedShapeId is specified", e.getMessage());
|
||||
}
|
||||
|
||||
public void testNoRelation() throws IOException {
|
||||
ShapeBuilder<?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null);
|
||||
GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(fieldName(), shape);
|
||||
GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> builder.relation(null));
|
||||
assertEquals("No Shape Relation defined", e.getMessage());
|
||||
}
|
||||
|
||||
public void testInvalidRelation() throws IOException {
|
||||
ShapeBuilder<?, ?> shape = RandomShapeGenerator.createShapeWithin(random(), null);
|
||||
GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
|
||||
builder.strategy(SpatialStrategy.TERM);
|
||||
expectThrows(IllegalArgumentException.class, () -> builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
|
||||
GeoShapeQueryBuilder builder2 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
|
||||
builder2.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN));
|
||||
expectThrows(IllegalArgumentException.class, () -> builder2.strategy(SpatialStrategy.TERM));
|
||||
GeoShapeQueryBuilder builder3 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
|
||||
builder3.strategy(SpatialStrategy.TERM);
|
||||
expectThrows(IllegalArgumentException.class, () -> builder3.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
|
||||
}
|
||||
|
||||
// see #3878
|
||||
public void testThatXContentSerializationInsideOfArrayWorks() throws Exception {
|
||||
EnvelopeBuilder envelopeBuilder = new EnvelopeBuilder(new Coordinate(0, 0), new Coordinate(10, 10));
|
||||
|
@ -198,7 +205,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
|||
|
||||
public void testFromJson() throws IOException {
|
||||
String json =
|
||||
"{\n" +
|
||||
"{\n" +
|
||||
" \"geo_shape\" : {\n" +
|
||||
" \"location\" : {\n" +
|
||||
" \"shape\" : {\n" +
|
||||
|
@ -223,7 +230,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
|||
UnsupportedOperationException e = expectThrows(UnsupportedOperationException.class, () -> query.toQuery(createShardContext()));
|
||||
assertEquals("query must be rewritten first", e.getMessage());
|
||||
QueryBuilder rewrite = rewriteAndFetch(query, createShardContext());
|
||||
GeoShapeQueryBuilder geoShapeQueryBuilder = new GeoShapeQueryBuilder(fieldName(), indexedShapeToReturn);
|
||||
GeoShapeQueryBuilder geoShapeQueryBuilder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeToReturn);
|
||||
geoShapeQueryBuilder.strategy(query.strategy());
|
||||
geoShapeQueryBuilder.relation(query.relation());
|
||||
assertEquals(geoShapeQueryBuilder, rewrite);
|
||||
|
@ -237,7 +244,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
|||
|
||||
builder = rewriteAndFetch(builder, createShardContext());
|
||||
|
||||
GeoShapeQueryBuilder expectedShape = new GeoShapeQueryBuilder(fieldName(), indexedShapeToReturn);
|
||||
GeoShapeQueryBuilder expectedShape = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeToReturn);
|
||||
expectedShape.strategy(shape.strategy());
|
||||
expectedShape.relation(shape.relation());
|
||||
QueryBuilder expected = new BoolQueryBuilder()
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
|
@ -62,7 +62,6 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuilder> {
|
||||
|
||||
@Override
|
||||
protected MatchQueryBuilder doCreateTestQueryBuilder() {
|
||||
String fieldName = randomFrom(STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME, BOOLEAN_FIELD_NAME, INT_FIELD_NAME,
|
||||
|
|
|
@ -1048,12 +1048,6 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
|
|||
"_field_names", "enabled=true"))),
|
||||
MapperService.MergeReason.MAPPING_UPDATE);
|
||||
}
|
||||
assertWarnings(new String[] {
|
||||
"Field parameter [tree_levels] is deprecated and will be removed in a future version.",
|
||||
"Field parameter [precision] is deprecated and will be removed in a future version.",
|
||||
"Field parameter [strategy] is deprecated and will be removed in a future version.",
|
||||
"Field parameter [distance_error_pct] is deprecated and will be removed in a future version."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -380,7 +380,6 @@ public class GeoFilterIT extends ESIntegTestCase {
|
|||
.endObject()
|
||||
.startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("ignore_malformed", true)
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
|
|
|
@ -45,21 +45,21 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
|
|||
public void testOrientationPersistence() throws Exception {
|
||||
String idxName = "orientation";
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("shape")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "left")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "left")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
// create index
|
||||
assertAcked(prepareCreate(idxName).addMapping("shape", mapping, XContentType.JSON));
|
||||
|
||||
mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("shape")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "right")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("orientation", "right")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
|
||||
assertAcked(prepareCreate(idxName+"2").addMapping("shape", mapping, XContentType.JSON));
|
||||
ensureGreen(idxName, idxName+"2");
|
||||
|
@ -144,8 +144,9 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
|
|||
|
||||
String source = "{\n" +
|
||||
" \"shape\" : {\n" +
|
||||
" \"type\" : \"bbox\",\n" +
|
||||
" \"coordinates\" : [[-45.0, 45.0], [45.0, -45.0]]\n" +
|
||||
" \"type\" : \"circle\",\n" +
|
||||
" \"coordinates\" : [-45.0, 45.0],\n" +
|
||||
" \"radius\" : \"100m\"\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
|
|
|
@ -19,21 +19,16 @@
|
|||
|
||||
package org.elasticsearch.search.geo;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
|
||||
import org.apache.lucene.geo.GeoTestUtil;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.CheckedSupplier;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.geo.SpatialStrategy;
|
||||
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
|
||||
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
|
||||
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
|
||||
import org.elasticsearch.common.geo.builders.LineStringBuilder;
|
||||
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
|
||||
import org.elasticsearch.common.geo.builders.PointBuilder;
|
||||
import org.elasticsearch.common.geo.builders.PolygonBuilder;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -41,9 +36,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.MapperParsingException;
|
||||
import org.elasticsearch.index.query.ExistsQueryBuilder;
|
||||
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
|
@ -70,26 +63,12 @@ import static org.hamcrest.Matchers.greaterThan;
|
|||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
||||
private static final String[] PREFIX_TREES = new String[] {
|
||||
LegacyGeoShapeFieldMapper.DeprecatedParameters.PrefixTrees.GEOHASH,
|
||||
LegacyGeoShapeFieldMapper.DeprecatedParameters.PrefixTrees.QUADTREE
|
||||
};
|
||||
|
||||
private XContentBuilder createMapping() throws Exception {
|
||||
XContentBuilder xcb = XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape");
|
||||
if (randomBoolean()) {
|
||||
xcb = xcb.field("tree", randomFrom(PREFIX_TREES))
|
||||
.field("strategy", randomFrom(SpatialStrategy.RECURSIVE, SpatialStrategy.TERM));
|
||||
}
|
||||
xcb = xcb.endObject().endObject().endObject().endObject();
|
||||
|
||||
return xcb;
|
||||
}
|
||||
|
||||
public void testNullShape() throws Exception {
|
||||
String mapping = Strings.toString(createMapping());
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
|
||||
ensureGreen();
|
||||
|
||||
|
@ -100,7 +79,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
|
||||
public void testIndexPointsFilterRectangle() throws Exception {
|
||||
String mapping = Strings.toString(createMapping());
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
|
||||
ensureGreen();
|
||||
|
||||
|
@ -142,11 +126,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
|
||||
public void testEdgeCases() throws Exception {
|
||||
XContentBuilder xcb = XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.endObject().endObject().endObject().endObject();
|
||||
String mapping = Strings.toString(xcb);
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
|
||||
ensureGreen();
|
||||
|
||||
|
@ -178,7 +163,12 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
|
||||
public void testIndexedShapeReference() throws Exception {
|
||||
String mapping = Strings.toString(createMapping());
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||
.startObject("properties").startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.endObject().endObject()
|
||||
.endObject().endObject());
|
||||
client().admin().indices().prepareCreate("test").addMapping("type1", mapping, XContentType.JSON).get();
|
||||
createIndex("shapes");
|
||||
ensureGreen();
|
||||
|
@ -215,7 +205,14 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
|
||||
public void testIndexedShapeReferenceSourceDisabled() throws Exception {
|
||||
XContentBuilder mapping = createMapping();
|
||||
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
|
||||
.startObject("properties")
|
||||
.startObject("location")
|
||||
.field("type", "geo_shape")
|
||||
.field("tree", "quadtree")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject();
|
||||
client().admin().indices().prepareCreate("test").addMapping("type1", mapping).get();
|
||||
createIndex("shapes", Settings.EMPTY, "shape_type", "_source", "enabled=false");
|
||||
ensureGreen();
|
||||
|
@ -329,107 +326,24 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
|||
assertHitCount(result, 1);
|
||||
}
|
||||
|
||||
public void testQueryRandomGeoCollection() throws Exception {
|
||||
public void testShapeFilterWithRandomGeoCollection() throws Exception {
|
||||
// Create a random geometry collection.
|
||||
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
|
||||
org.apache.lucene.geo.Polygon randomPoly = GeoTestUtil.nextPolygon();
|
||||
CoordinatesBuilder cb = new CoordinatesBuilder();
|
||||
for (int i = 0; i < randomPoly.numPoints(); ++i) {
|
||||
cb.coordinate(randomPoly.getPolyLon(i), randomPoly.getPolyLat(i));
|
||||
}
|
||||
gcb.shape(new PolygonBuilder(cb));
|
||||
|
||||
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
|
||||
|
||||
if (randomBoolean()) {
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.addMapping("type", "location", "type=geo_shape").get();
|
||||
} else {
|
||||
client().admin().indices().prepareCreate("test")
|
||||
.addMapping("type", "location", "type=geo_shape,tree=quadtree").get();
|
||||
}
|
||||
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
|
||||
.get();
|
||||
|
||||
XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("location"), null).endObject();
|
||||
client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
|
||||
|
||||
ShapeBuilder filterShape = (gcb.getShapeAt(gcb.numShapes() - 1));
|
||||
ShapeBuilder filterShape = (gcb.getShapeAt(randomIntBetween(0, gcb.numShapes() - 1)));
|
||||
|
||||
GeoShapeQueryBuilder geoShapeQueryBuilder = QueryBuilders.geoShapeQuery("location", filterShape);
|
||||
geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS);
|
||||
SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get();
|
||||
assertSearchResponse(result);
|
||||
assertHitCount(result, 1);
|
||||
}
|
||||
|
||||
public void testRandomGeoCollectionQuery() throws Exception {
|
||||
boolean usePrefixTrees = randomBoolean();
|
||||
// Create a random geometry collection to index.
|
||||
GeometryCollectionBuilder gcb;
|
||||
if (usePrefixTrees) {
|
||||
gcb = RandomShapeGenerator.createGeometryCollection(random());
|
||||
} else {
|
||||
// vector strategy does not yet support multipoint queries
|
||||
gcb = new GeometryCollectionBuilder();
|
||||
int numShapes = RandomNumbers.randomIntBetween(random(), 1, 4);
|
||||
for (int i = 0; i < numShapes; ++i) {
|
||||
ShapeBuilder shape;
|
||||
do {
|
||||
shape = RandomShapeGenerator.createShape(random());
|
||||
} while (shape instanceof MultiPointBuilder);
|
||||
gcb.shape(shape);
|
||||
}
|
||||
}
|
||||
org.apache.lucene.geo.Polygon randomPoly = GeoTestUtil.nextPolygon();
|
||||
CoordinatesBuilder cb = new CoordinatesBuilder();
|
||||
for (int i = 0; i < randomPoly.numPoints(); ++i) {
|
||||
cb.coordinate(randomPoly.getPolyLon(i), randomPoly.getPolyLat(i));
|
||||
}
|
||||
gcb.shape(new PolygonBuilder(cb));
|
||||
|
||||
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
|
||||
|
||||
if (usePrefixTrees == false) {
|
||||
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape")
|
||||
.execute().actionGet();
|
||||
} else {
|
||||
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
|
||||
.execute().actionGet();
|
||||
}
|
||||
|
||||
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();
|
||||
GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", filterShape);
|
||||
filter.relation(ShapeRelation.INTERSECTS);
|
||||
SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(QueryBuilders.matchAllQuery())
|
||||
.setPostFilter(filter).get();
|
||||
assertSearchResponse(result);
|
||||
assertHitCount(result, 1);
|
||||
}
|
||||
|
@ -461,28 +375,6 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
|||
assertThat(response.getHits().getTotalHits().value, greaterThan(0L));
|
||||
}
|
||||
|
||||
public void testExistsQuery() throws Exception {
|
||||
// Create a random geometry collection.
|
||||
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
|
||||
logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes());
|
||||
|
||||
if (randomBoolean()) {
|
||||
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape")
|
||||
.execute().actionGet();
|
||||
} else {
|
||||
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
|
||||
.execute().actionGet();
|
||||
}
|
||||
|
||||
XContentBuilder docSource = gcb.toXContent(jsonBuilder().startObject().field("location"), null).endObject();
|
||||
client().prepareIndex("test", "type", "1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
|
||||
|
||||
ExistsQueryBuilder eqb = QueryBuilders.existsQuery("location");
|
||||
SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(eqb).get();
|
||||
assertSearchResponse(result);
|
||||
assertHitCount(result, 1);
|
||||
}
|
||||
|
||||
public void testShapeFilterWithDefinedGeoCollection() throws Exception {
|
||||
createIndex("shapes");
|
||||
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape,tree=quadtree")
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -32,7 +32,6 @@ import org.elasticsearch.common.geo.builders.MultiPointBuilder;
|
|||
import org.elasticsearch.common.geo.builders.PointBuilder;
|
||||
import org.elasticsearch.common.geo.builders.PolygonBuilder;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.search.geo.GeoShapeQueryTests;
|
||||
import org.junit.Assert;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
|
@ -154,7 +153,6 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
|
|||
/**
|
||||
* Creates a random shape useful for randomized testing, NOTE: exercise caution when using this to build random GeometryCollections
|
||||
* as creating a large random number of random shapes can result in massive resource consumption
|
||||
* see: {@link GeoShapeQueryTests#testQueryRandomGeoCollection()}
|
||||
*
|
||||
* The following options are included
|
||||
* @param nearPoint Create a shape near a provided point
|
||||
|
|
|
@ -113,7 +113,6 @@ public abstract class AbstractBuilderTestCase extends ESTestCase {
|
|||
protected static final String GEO_POINT_FIELD_NAME = "mapped_geo_point";
|
||||
protected static final String GEO_POINT_ALIAS_FIELD_NAME = "mapped_geo_point_alias";
|
||||
protected static final String GEO_SHAPE_FIELD_NAME = "mapped_geo_shape";
|
||||
protected static final String LEGACY_GEO_SHAPE_FIELD_NAME = "mapped_legacy_geo_shape";
|
||||
protected static final String[] MAPPED_FIELD_NAMES = new String[]{STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME,
|
||||
INT_FIELD_NAME, INT_RANGE_FIELD_NAME, DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME,
|
||||
DATE_RANGE_FIELD_NAME, OBJECT_FIELD_NAME, GEO_POINT_FIELD_NAME, GEO_POINT_ALIAS_FIELD_NAME,
|
||||
|
@ -218,28 +217,12 @@ public abstract class AbstractBuilderTestCase extends ESTestCase {
|
|||
AbstractBuilderTestCase.this, false);
|
||||
return null;
|
||||
});
|
||||
if (enableWarningsCheck() == true) {
|
||||
assertDeprecatedGeoWarnings();
|
||||
}
|
||||
}
|
||||
|
||||
serviceHolder.clientInvocationHandler.delegate = this;
|
||||
serviceHolderWithNoType.clientInvocationHandler.delegate = this;
|
||||
}
|
||||
|
||||
protected void assertDeprecatedGeoWarnings() {
|
||||
String prefix = "Field parameter [";
|
||||
String postfix = "] is deprecated and will be removed in a future version.";
|
||||
String[] deprecationWarnings = new String[] {
|
||||
prefix + "tree" + postfix,
|
||||
prefix + "tree_levels" + postfix,
|
||||
prefix + "precision" + postfix,
|
||||
prefix + "strategy" + postfix,
|
||||
prefix + "distance_error_pct" + postfix
|
||||
};
|
||||
assertWarnings(deprecationWarnings);
|
||||
}
|
||||
|
||||
protected static SearchContext getSearchContext(QueryShardContext context) {
|
||||
TestSearchContext testSearchContext = new TestSearchContext(context) {
|
||||
@Override
|
||||
|
@ -413,8 +396,7 @@ public abstract class AbstractBuilderTestCase extends ESTestCase {
|
|||
OBJECT_FIELD_NAME, "type=object",
|
||||
GEO_POINT_FIELD_NAME, "type=geo_point",
|
||||
GEO_POINT_ALIAS_FIELD_NAME, "type=alias,path=" + GEO_POINT_FIELD_NAME,
|
||||
GEO_SHAPE_FIELD_NAME, "type=geo_shape",
|
||||
LEGACY_GEO_SHAPE_FIELD_NAME, "type=geo_shape,tree=quadtree"
|
||||
GEO_SHAPE_FIELD_NAME, "type=geo_shape"
|
||||
))), MapperService.MergeReason.MAPPING_UPDATE);
|
||||
// also add mappings for two inner field in the object field
|
||||
mapperService.merge("_doc", new CompressedXContent("{\"properties\":{\"" + OBJECT_FIELD_NAME + "\":{\"type\":\"object\","
|
||||
|
|
Loading…
Reference in New Issue