Geo: refactor geo mapper and query builder (#44884)

Refactors out the indexing and query generation logic out of the
mapper and query builder into a separate unit-testable classes.
This commit is contained in:
Igor Motov 2019-07-26 12:14:05 -04:00
parent 1561ab5420
commit cfc8d17bb4
20 changed files with 648 additions and 441 deletions

View File

@ -29,7 +29,7 @@ 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.AbstractGeometryFieldMapper;
import org.locationtech.jts.geom.Coordinate;
import java.io.IOException;
@ -42,7 +42,7 @@ 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, AbstractGeometryFieldMapper shapeMapper)
throws IOException {
GeoShapeType shapeType = null;
DistanceUnit.Distance radius = null;
@ -50,13 +50,13 @@ abstract class GeoJsonParser {
GeometryCollectionBuilder geometryCollections = null;
Orientation orientation = (shapeMapper == null)
? BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value()
? AbstractGeometryFieldMapper.Defaults.ORIENTATION.value()
: shapeMapper.orientation();
Explicit<Boolean> coerce = (shapeMapper == null)
? BaseGeoShapeFieldMapper.Defaults.COERCE
? AbstractGeometryFieldMapper.Defaults.COERCE
: shapeMapper.coerce();
Explicit<Boolean> ignoreZValue = (shapeMapper == null)
? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE
? AbstractGeometryFieldMapper.Defaults.IGNORE_Z_VALUE
: shapeMapper.ignoreZValue();
String malformedException = null;
@ -208,7 +208,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, AbstractGeometryFieldMapper mapper) throws
IOException {
if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
throw new ElasticsearchParseException("geometries must be an array of geojson objects");

View File

@ -34,7 +34,7 @@ import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
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 AbstractGeometryFieldMapper 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 AbstractGeometryFieldMapper 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) ? AbstractGeometryFieldMapper.Defaults.IGNORE_Z_VALUE :
shapeMapper.ignoreZValue();
Explicit<Boolean> coerce = (shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
Explicit<Boolean> coerce = (shapeMapper == null) ? AbstractGeometryFieldMapper.Defaults.COERCE : shapeMapper.coerce();
// setup the tokenizer; configured to read words w/o numbers
StreamTokenizer tokenizer = new StreamTokenizer(reader);
tokenizer.resetSyntax();
@ -258,7 +258,7 @@ public class GeoWKTParser {
return null;
}
PolygonBuilder builder = new PolygonBuilder(parseLinearRing(stream, ignoreZValue, coerce),
BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value());
AbstractGeometryFieldMapper.Defaults.ORIENTATION.value());
while (nextCloserOrComma(stream).equals(COMMA)) {
builder.hole(parseLinearRing(stream, ignoreZValue, coerce));
}

View File

@ -26,7 +26,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.MapXContentParser;
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import java.io.IOException;
import java.util.Collections;
@ -50,7 +50,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, AbstractGeometryFieldMapper shapeMapper) throws IOException {
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
return null;
} if (parser.currentToken() == XContentParser.Token.START_OBJECT) {

View File

@ -26,17 +26,22 @@ import org.apache.lucene.search.TermQuery;
import org.elasticsearch.Version;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.geo.geometry.Geometry;
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.text.ParseException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -47,8 +52,8 @@ import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_MA
/**
* Base class for {@link GeoShapeFieldMapper} and {@link LegacyGeoShapeFieldMapper}
*/
public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
public static final String CONTENT_TYPE = "geo_shape";
public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends FieldMapper {
public static class Names {
public static final ParseField ORIENTATION = new ParseField("orientation");
@ -62,7 +67,36 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
public static final Explicit<Boolean> IGNORE_Z_VALUE = new Explicit<>(true, false);
}
public abstract static class Builder<T extends Builder, Y extends BaseGeoShapeFieldMapper>
/**
* Interface representing an preprocessor in geo-shape indexing pipeline
*/
public interface Indexer<Parsed, Processed> {
Processed prepareForIndexing(Parsed geometry);
Class<Processed> processedClass();
}
/**
* interface representing parser in geo shape indexing pipeline
*/
public interface Parser<Parsed> {
Parsed parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException;
}
/**
* interface representing a query builder that generates a query from the given shape
*/
public interface QueryProcessor {
Query process(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation, QueryShardContext context);
}
public abstract static class Builder<T extends Builder, Y extends AbstractGeometryFieldMapper>
extends FieldMapper.Builder<T, Y> {
protected Boolean coerce;
protected Boolean ignoreMalformed;
@ -152,7 +186,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
throw new IllegalArgumentException("name cannot be empty string");
}
BaseGeoShapeFieldType ft = (BaseGeoShapeFieldType)fieldType();
AbstractGeometryFieldType ft = (AbstractGeometryFieldType)fieldType();
ft.setOrientation(orientation().value());
}
}
@ -218,10 +252,16 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
}
}
public abstract static class BaseGeoShapeFieldType extends MappedFieldType {
public abstract static class AbstractGeometryFieldType<Parsed, Processed> extends MappedFieldType {
protected Orientation orientation = Defaults.ORIENTATION.value();
protected BaseGeoShapeFieldType() {
protected Indexer<Parsed, Processed> geometryIndexer;
protected Parser<Parsed> geometryParser;
protected QueryProcessor geometryQueryBuilder;
protected AbstractGeometryFieldType() {
setIndexOptions(IndexOptions.DOCS);
setTokenized(false);
setStored(false);
@ -229,7 +269,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
setOmitNorms(true);
}
protected BaseGeoShapeFieldType(BaseGeoShapeFieldType ref) {
protected AbstractGeometryFieldType(AbstractGeometryFieldType ref) {
super(ref);
this.orientation = ref.orientation;
}
@ -237,7 +277,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
@Override
public boolean equals(Object o) {
if (!super.equals(o)) return false;
BaseGeoShapeFieldType that = (BaseGeoShapeFieldType) o;
AbstractGeometryFieldType that = (AbstractGeometryFieldType) o;
return orientation == that.orientation;
}
@ -246,16 +286,6 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
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) {
@ -272,16 +302,40 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
public Query termQuery(Object value, QueryShardContext context) {
throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead");
}
public void setGeometryIndexer(Indexer<Parsed, Processed> geometryIndexer) {
this.geometryIndexer = geometryIndexer;
}
protected Indexer<Parsed, Processed> geometryIndexer() {
return geometryIndexer;
}
public void setGeometryParser(Parser<Parsed> geometryParser) {
this.geometryParser = geometryParser;
}
protected Parser<Parsed> geometryParser() {
return geometryParser;
}
public void setGeometryQueryBuilder(QueryProcessor geometryQueryBuilder) {
this.geometryQueryBuilder = geometryQueryBuilder;
}
public QueryProcessor geometryQueryBuilder() {
return geometryQueryBuilder;
}
}
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) {
protected AbstractGeometryFieldMapper(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;
@ -291,7 +345,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
@Override
protected void doMerge(Mapper mergeWith) {
super.doMerge(mergeWith);
BaseGeoShapeFieldMapper gsfm = (BaseGeoShapeFieldMapper)mergeWith;
AbstractGeometryFieldMapper gsfm = (AbstractGeometryFieldMapper)mergeWith;
if (gsfm.coerce.explicit()) {
this.coerce = gsfm.coerce;
}
@ -310,7 +364,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
@Override
protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
builder.field("type", contentType());
BaseGeoShapeFieldType ft = (BaseGeoShapeFieldType)fieldType();
AbstractGeometryFieldType ft = (AbstractGeometryFieldType)fieldType();
if (includeDefaults || ft.orientation() != Defaults.ORIENTATION.value()) {
builder.field(Names.ORIENTATION.getPreferredName(), ft.orientation());
}
@ -338,11 +392,35 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
}
public Orientation orientation() {
return ((BaseGeoShapeFieldType)fieldType).orientation();
return ((AbstractGeometryFieldType)fieldType).orientation();
}
protected abstract void indexShape(ParseContext context, Processed shape);
/** parsing logic for geometry indexing */
@Override
protected String contentType() {
return CONTENT_TYPE;
public void parse(ParseContext context) throws IOException {
AbstractGeometryFieldType fieldType = (AbstractGeometryFieldType)fieldType();
@SuppressWarnings("unchecked") Indexer<Parsed, Processed> geometryIndexer = fieldType.geometryIndexer();
@SuppressWarnings("unchecked") Parser<Parsed> geometryParser = fieldType.geometryParser();
try {
Processed shape = context.parseExternalValue(geometryIndexer.processedClass());
if (shape == null) {
Parsed geometry = geometryParser.parse(context.parser(), this);
if (geometry == null) {
return;
}
shape = geometryIndexer.prepareForIndexing(geometry);
}
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());
}
}
}

View File

@ -24,7 +24,6 @@ import org.apache.lucene.geo.Line;
import org.apache.lucene.geo.Polygon;
import org.apache.lucene.index.IndexableField;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.geo.GeometryIndexer;
import org.elasticsearch.common.geo.GeometryParser;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.settings.Settings;
@ -37,8 +36,8 @@ import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -62,9 +61,10 @@ import java.util.Arrays;
* <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 AbstractGeometryFieldMapper<Geometry, Geometry> {
public static final String CONTENT_TYPE = "geo_shape";
public static class Builder extends BaseGeoShapeFieldMapper.Builder<BaseGeoShapeFieldMapper.Builder, GeoShapeFieldMapper> {
public static class Builder extends AbstractGeometryFieldMapper.Builder<AbstractGeometryFieldMapper.Builder, GeoShapeFieldMapper> {
public Builder(String name) {
super (name, new GeoShapeFieldType(), new GeoShapeFieldType());
}
@ -75,9 +75,21 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
return new GeoShapeFieldMapper(name, fieldType, defaultFieldType, ignoreMalformed(context), coerce(context),
ignoreZValue(), context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);
}
@Override
protected void setupFieldType(BuilderContext context) {
super.setupFieldType(context);
GeometryParser geometryParser = new GeometryParser(orientation == ShapeBuilder.Orientation.RIGHT, coerce(context).value(),
ignoreZValue().value());
((GeoShapeFieldType)fieldType()).setGeometryIndexer(new GeoShapeIndexer(orientation == ShapeBuilder.Orientation.RIGHT));
((GeoShapeFieldType)fieldType()).setGeometryParser( (parser, mapper) -> geometryParser.parse(parser));
((GeoShapeFieldType)fieldType()).setGeometryQueryBuilder(new VectorGeoShapeQueryProcessor());
}
}
public static final class GeoShapeFieldType extends BaseGeoShapeFieldType {
public static final class GeoShapeFieldType extends AbstractGeometryFieldType<Geometry, Geometry> {
public GeoShapeFieldType() {
super();
}
@ -90,10 +102,17 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
public GeoShapeFieldType clone() {
return new GeoShapeFieldType(this);
}
}
private final GeometryParser geometryParser;
private final GeometryIndexer geometryIndexer;
@Override
public String typeName() {
return CONTENT_TYPE;
}
@Override
protected Indexer<Geometry, Geometry> geometryIndexer() {
return new GeoShapeIndexer(orientation == ShapeBuilder.Orientation.RIGHT);
}
}
public GeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce,
@ -101,8 +120,6 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
MultiFields multiFields, CopyTo copyTo) {
super(simpleName, fieldType, defaultFieldType, ignoreMalformed, coerce, ignoreZValue, indexSettings,
multiFields, copyTo);
geometryParser = new GeometryParser(orientation() == ShapeBuilder.Orientation.RIGHT, coerce().value(), ignoreZValue.value());
geometryIndexer = new GeometryIndexer(true);
}
@Override
@ -110,35 +127,9 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
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);
if (shape == null) {
Geometry geometry = geometryParser.parse(context.parser());
if (geometry == null) {
return;
}
shape = geometryIndexer.prepareForIndexing(geometry);
}
indexShape(context, shape);
} catch (Exception e) {
if (ignoreMalformed.value() == false) {
throw new MapperParsingException("failed to parse field [{}] of type [{}]", e, fieldType().name(),
fieldType().typeName());
}
context.addIgnoredField(fieldType().name());
}
}
private void indexShape(ParseContext context, Object luceneShape) {
if (luceneShape instanceof Geometry) {
((Geometry) luceneShape).visit(new LuceneGeometryIndexer(context));
} else {
throw new IllegalArgumentException("invalid shape type found [" + luceneShape.getClass() + "] while indexing shape");
}
protected void indexShape(ParseContext context, Geometry luceneShape) {
luceneShape.visit(new LuceneGeometryIndexer(context));
}
private class LuceneGeometryIndexer implements GeometryVisitor<Void, RuntimeException> {
@ -232,4 +223,9 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
context.doc().add(f);
}
}
@Override
protected String contentType() {
return CONTENT_TYPE;
}
}

View File

@ -18,7 +18,7 @@
*/
package org.elasticsearch.common.geo;
package org.elasticsearch.index.mapper;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.geo.geometry.Circle;
@ -52,7 +52,7 @@ import static org.elasticsearch.common.geo.GeoUtils.normalizeLon;
/**
* Utility class that converts geometries into Lucene-compatible form
*/
public final class GeometryIndexer {
public final class GeoShapeIndexer implements AbstractGeometryFieldMapper.Indexer<Geometry, Geometry> {
private static final double DATELINE = 180;
@ -60,7 +60,7 @@ public final class GeometryIndexer {
private final boolean orientation;
public GeometryIndexer(boolean orientation) {
public GeoShapeIndexer(boolean orientation) {
this.orientation = orientation;
}
@ -176,6 +176,11 @@ public final class GeometryIndexer {
});
}
@Override
public Class<Geometry> processedClass() {
return Geometry.class;
}
/**
* Calculate the intersection of a line segment and a vertical dateline.
*
@ -666,10 +671,8 @@ public final class GeometryIndexer {
* Array of edges will be ordered asc by the y-coordinate of the
* intersections of edges.
*
* @param dateline
* x-coordinate of the dateline
* @param edges
* set of edges that may intersect with the dateline
* @param dateline x-coordinate of the dateline
* @param edges set of edges that may intersect with the dateline
* @return number of intersecting edges
*/
protected static int intersections(double dateline, Edge[] edges) {
@ -697,10 +700,10 @@ public final class GeometryIndexer {
for (int i = 0; i < edges.length; i++) {
if (edges[i].component >= 0) {
double[] partitionPoint = new double[3];
int length = component(edges[i], -(components.size()+numHoles+1), mainEdges, partitionPoint);
double[] partitionPoint = new double[3];
int length = component(edges[i], -(components.size() + numHoles + 1), mainEdges, partitionPoint);
List<Point[]> component = new ArrayList<>();
component.add(coordinates(edges[i], new Point[length+1], partitionPoint));
component.add(coordinates(edges[i], new Point[length + 1], partitionPoint));
components.add(component);
}
}
@ -781,22 +784,22 @@ public final class GeometryIndexer {
* This method sets the component id of all edges in a ring to a given id and shifts the
* coordinates of this component according to the dateline
*
* @param edge An arbitrary edge of the component
* @param id id to apply to the component
* @param edge An arbitrary edge of the component
* @param id id to apply to the component
* @param edges a list of edges to which all edges of the component will be added (could be <code>null</code>)
* @return number of edges that belong to this component
*/
private static int component(final Edge edge, final int id, final ArrayList<Edge> edges, double[] partitionPoint) {
// find a coordinate that is not part of the dateline
Edge any = edge;
while(any.coordinate.getLon() == +DATELINE || any.coordinate.getLon() == -DATELINE) {
if((any = any.next) == edge) {
while (any.coordinate.getLon() == +DATELINE || any.coordinate.getLon() == -DATELINE) {
if ((any = any.next) == edge) {
break;
}
}
double shiftOffset = any.coordinate.getLon() > DATELINE ? DATELINE : (any.coordinate.getLon() < -DATELINE ? -DATELINE : 0);
// run along the border of the component, collect the
// edges, shift them according to the dateline and
// update the component id
@ -847,14 +850,15 @@ public final class GeometryIndexer {
prev = current;
}
length++;
} while(connectedComponents == 0 && (current = current.next) != edge);
} while (connectedComponents == 0 && (current = current.next) != edge);
return (splitIndex != 1) ? length-splitIndex: length;
return (splitIndex != 1) ? length - splitIndex : length;
}
/**
* Compute all coordinates of a component
* @param component an arbitrary edge of the component
*
* @param component an arbitrary edge of the component
* @param coordinates Array of coordinates to write the result to
* @return the coordinates parameter
*/
@ -875,8 +879,8 @@ public final class GeometryIndexer {
return coordinates;
}
private static List<Polygon> buildPoints(List<List<Point[]>> components) {
List<Polygon> result = new ArrayList<>(components.size());
private static List<Polygon> buildPoints(List<List<Point[]>> components) {
List<Polygon> result = new ArrayList<>(components.size());
for (int i = 0; i < components.size(); i++) {
List<Point[]> component = components.get(i);
result.add(buildPolygon(component));
@ -885,7 +889,7 @@ public final class GeometryIndexer {
}
private static Polygon buildPolygon(List<Point[]> polygon) {
List<org.elasticsearch.geo.geometry.LinearRing> holes;
List<LinearRing> holes;
Point[] shell = polygon.get(0);
if (polygon.size() > 1) {
holes = new ArrayList<>(polygon.size() - 1);
@ -899,7 +903,7 @@ public final class GeometryIndexer {
x[c] = normalizeLon(coords[c].getLon());
y[c] = normalizeLat(coords[c].getLat());
}
holes.add(new org.elasticsearch.geo.geometry.LinearRing(y, x));
holes.add(new LinearRing(y, x));
}
} else {
holes = Collections.emptyList();
@ -924,11 +928,12 @@ public final class GeometryIndexer {
final Point[][] points = new Point[numHoles][];
for (int i = 0; i < numHoles; i++) {
double[] partitionPoint = new double[3];
int length = component(holes[i], -(i+1), null, partitionPoint); // mark as visited by inverting the sign
points[i] = coordinates(holes[i], new Point[length+1], partitionPoint);
double[] partitionPoint = new double[3];
int length = component(holes[i], -(i + 1), null, partitionPoint); // mark as visited by inverting the sign
points[i] = coordinates(holes[i], new Point[length + 1], partitionPoint);
}
return points;
}
}

View File

@ -46,6 +46,7 @@ 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.elasticsearch.index.query.LegacyGeoShapeQueryProcessor;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.jts.JtsGeometry;
@ -79,7 +80,7 @@ import java.util.Objects;
* @deprecated use {@link GeoShapeFieldMapper}
*/
@Deprecated
public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
public class LegacyGeoShapeFieldMapper extends AbstractGeometryFieldMapper<ShapeBuilder<?, ?, ?>, Shape> {
public static final String CONTENT_TYPE = "geo_shape";
@ -183,7 +184,8 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
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> {
public static class Builder extends AbstractGeometryFieldMapper.Builder<AbstractGeometryFieldMapper.Builder,
LegacyGeoShapeFieldMapper> {
DeprecatedParameters deprecatedParameters;
@ -270,6 +272,10 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
protected void setupFieldType(BuilderContext context) {
super.setupFieldType(context);
fieldType().setGeometryIndexer(new LegacyGeoShapeIndexer());
fieldType().setGeometryParser(ShapeParser::parse);
fieldType().setGeometryQueryBuilder(new LegacyGeoShapeQueryProcessor(fieldType()));
// field mapper handles this at build time
// but prefix tree strategies require a name, so throw a similar exception
if (fieldType().name().isEmpty()) {
@ -299,7 +305,7 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
}
}
public static final class GeoShapeFieldType extends BaseGeoShapeFieldType {
public static final class GeoShapeFieldType extends AbstractGeometryFieldType {
private String tree = DeprecatedParameters.Defaults.TREE;
private SpatialStrategy strategy = DeprecatedParameters.Defaults.STRATEGY;
@ -357,6 +363,11 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
defaultDistanceErrorPct);
}
@Override
public String typeName() {
return CONTENT_TYPE;
}
@Override
public void checkCompatibility(MappedFieldType fieldType, List<String> conflicts) {
super.checkCompatibility(fieldType, conflicts);
@ -479,42 +490,26 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
}
@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;
protected void indexShape(ParseContext context, Shape shape) {
if (fieldType().pointsOnly() == true) {
// index configured for pointsOnly
if (shape instanceof XShapeCollection && XShapeCollection.class.cast(shape).pointsOnly()) {
// MULTIPOINT data: index each point separately
@SuppressWarnings("unchecked") List<Shape> shapes = ((XShapeCollection) shape).getShapes();
for (Shape s : shapes) {
doIndexShape(context, s);
}
shape = shapeBuilder.buildS4J();
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");
}
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());
}
doIndexShape(context, shape);
}
private void indexShape(ParseContext context, Shape shape) {
private void doIndexShape(ParseContext context, Shape shape) {
List<IndexableField> fields = new ArrayList<>(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(shape)));
createFieldNamesField(context, fields);
for (IndexableField field : fields) {
@ -574,4 +569,9 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
}
}
}
@Override
protected String contentType() {
return CONTENT_TYPE;
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.builders.ShapeBuilder;
import org.locationtech.spatial4j.shape.Shape;
public class LegacyGeoShapeIndexer implements AbstractGeometryFieldMapper.Indexer<ShapeBuilder<?, ?, ?>, Shape> {
@Override
public Shape prepareForIndexing(ShapeBuilder<?, ?, ?> shapeBuilder) {
return shapeBuilder.buildS4J();
}
@Override
public Class<Shape> processedClass() {
return Shape.class;
}
}

View File

@ -20,33 +20,13 @@
package org.elasticsearch.index.query;
import org.apache.logging.log4j.LogManager;
import org.apache.lucene.document.LatLonShape;
import org.apache.lucene.geo.Line;
import org.apache.lucene.geo.Polygon;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.GeometryIndexer;
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.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput;
@ -54,32 +34,17 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geo.geometry.Circle;
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.GeometryCollection;
import org.elasticsearch.geo.geometry.GeometryVisitor;
import org.elasticsearch.geo.geometry.LinearRing;
import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.geo.geometry.Rectangle;
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Shape;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import static org.elasticsearch.index.mapper.GeoShapeFieldMapper.toLucenePolygon;
/**
* Derived {@link AbstractGeometryQueryBuilder} that builds a lat, lon GeoShape Query
*/
@ -217,12 +182,12 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
@Override
protected List validContentTypes() {
return Arrays.asList(BaseGeoShapeFieldMapper.CONTENT_TYPE);
return Arrays.asList(GeoShapeFieldMapper.CONTENT_TYPE);
}
@Override
public String queryFieldType() {
return BaseGeoShapeFieldMapper.CONTENT_TYPE;
return GeoShapeFieldMapper.CONTENT_TYPE;
}
@Override
@ -245,260 +210,13 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
@Override
public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) {
if (fieldType.typeName().equals(BaseGeoShapeFieldMapper.CONTENT_TYPE) == false) {
if (fieldType.typeName().equals(GeoShapeFieldMapper.CONTENT_TYPE) == false) {
throw new QueryShardException(context,
"Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]");
}
final BaseGeoShapeFieldMapper.BaseGeoShapeFieldType ft = (BaseGeoShapeFieldMapper.BaseGeoShapeFieldType) fieldType;
Query query;
if (strategy != null || ft instanceof LegacyGeoShapeFieldMapper.GeoShapeFieldType) {
LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (LegacyGeoShapeFieldMapper.GeoShapeFieldType) ft;
SpatialStrategy spatialStrategy = shapeFieldType.strategy();
if (this.strategy != null) {
spatialStrategy = this.strategy;
}
PrefixTreeStrategy prefixTreeStrategy = shapeFieldType.resolvePrefixTreeStrategy(spatialStrategy);
if (prefixTreeStrategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) {
// this strategy doesn't support disjoint anymore: but it did
// before, including creating lucene fieldcache (!)
// in this case, execute disjoint as exists && !intersects
BooleanQuery.Builder bool = new BooleanQuery.Builder();
Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
Query intersects = prefixTreeStrategy.makeQuery(getArgs(shape, 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(shape, relation)));
}
} else {
query = new ConstantScoreQuery(getVectorQuery(context, shape));
}
return query;
}
public static SpatialArgs getArgs(Geometry shape, ShapeRelation relation) {
switch (relation) {
case DISJOINT:
return new SpatialArgs(SpatialOperation.IsDisjointTo, buildS4J(shape));
case INTERSECTS:
return new SpatialArgs(SpatialOperation.Intersects, buildS4J(shape));
case WITHIN:
return new SpatialArgs(SpatialOperation.IsWithin, buildS4J(shape));
case CONTAINS:
return new SpatialArgs(SpatialOperation.Contains, buildS4J(shape));
default:
throw new IllegalArgumentException("invalid relation [" + relation + "]");
}
}
/**
* Builds JTS shape from a geometry
*
* This method is needed to handle legacy indices and will be removed when we no longer need to build JTS shapes
*/
private static Shape buildS4J(Geometry geometry) {
return geometryToShapeBuilder(geometry).buildS4J();
}
private Query getVectorQuery(QueryShardContext context, Geometry queryShape) {
// 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, queryShape);
}
protected Query getVectorQueryFromShape(QueryShardContext context, Geometry queryShape) {
// TODO: Move this to QueryShardContext
GeometryIndexer geometryIndexer = new GeometryIndexer(true);
Geometry processedShape = geometryIndexer.prepareForIndexing(queryShape);
if (processedShape == null) {
return new MatchNoDocsQuery();
}
return queryShape.visit(new ShapeVisitor(context));
}
public static ShapeBuilder<?, ?, ?> geometryToShapeBuilder(Geometry geometry) {
ShapeBuilder<?, ?, ?> shapeBuilder = geometry.visit(new GeometryVisitor<ShapeBuilder<?, ?, ?>, RuntimeException>() {
@Override
public ShapeBuilder<?, ?, ?> visit(Circle circle) {
throw new UnsupportedOperationException("circle is not supported");
}
@Override
public ShapeBuilder<?, ?, ?> visit(GeometryCollection<?> collection) {
GeometryCollectionBuilder shapes = new GeometryCollectionBuilder();
for (Geometry geometry : collection) {
shapes.shape(geometry.visit(this));
}
return shapes;
}
@Override
public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Line line) {
List<Coordinate> coordinates = new ArrayList<>();
for (int i = 0; i < line.length(); i++) {
coordinates.add(new Coordinate(line.getLon(i), line.getLat(i), line.getAlt(i)));
}
return new LineStringBuilder(coordinates);
}
@Override
public ShapeBuilder<?, ?, ?> visit(LinearRing ring) {
throw new UnsupportedOperationException("circle is not supported");
}
@Override
public ShapeBuilder<?, ?, ?> visit(MultiLine multiLine) {
MultiLineStringBuilder lines = new MultiLineStringBuilder();
for (int i = 0; i < multiLine.size(); i++) {
lines.linestring((LineStringBuilder) visit(multiLine.get(i)));
}
return lines;
}
@Override
public ShapeBuilder<?, ?, ?> visit(MultiPoint multiPoint) {
List<Coordinate> coordinates = new ArrayList<>();
for (int i = 0; i < multiPoint.size(); i++) {
Point p = multiPoint.get(i);
coordinates.add(new Coordinate(p.getLon(), p.getLat(), p.getAlt()));
}
return new MultiPointBuilder(coordinates);
}
@Override
public ShapeBuilder<?, ?, ?> visit(MultiPolygon multiPolygon) {
MultiPolygonBuilder polygons = new MultiPolygonBuilder();
for (int i = 0; i < multiPolygon.size(); i++) {
polygons.polygon((PolygonBuilder) visit(multiPolygon.get(i)));
}
return polygons;
}
@Override
public ShapeBuilder<?, ?, ?> visit(Point point) {
return new PointBuilder(point.getLon(), point.getLat());
}
@Override
public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Polygon polygon) {
PolygonBuilder polygonBuilder =
new PolygonBuilder((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getPolygon()),
ShapeBuilder.Orientation.RIGHT, false);
for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
polygonBuilder.hole((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getHole(i)));
}
return polygonBuilder;
}
@Override
public ShapeBuilder<?, ?, ?> visit(Rectangle rectangle) {
return new EnvelopeBuilder(new Coordinate(rectangle.getMinLon(), rectangle.getMaxLat()),
new Coordinate(rectangle.getMaxLon(), rectangle.getMinLat()));
}
});
return shapeBuilder;
}
private class ShapeVisitor implements GeometryVisitor<Query, RuntimeException> {
QueryShardContext context;
MappedFieldType fieldType;
ShapeVisitor(QueryShardContext context) {
this.context = context;
this.fieldType = context.fieldMapper(fieldName);
}
@Override
public Query visit(Circle circle) {
throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape Circle");
}
@Override
public Query visit(GeometryCollection<?> collection) {
BooleanQuery.Builder bqb = new BooleanQuery.Builder();
visit(bqb, collection);
return bqb.build();
}
private void visit(BooleanQuery.Builder bqb, GeometryCollection<?> collection) {
for (Geometry shape : collection) {
if (shape instanceof MultiPoint) {
// Flatten multipoints
visit(bqb, (GeometryCollection<?>) shape);
} else {
bqb.add(shape.visit(this), BooleanClause.Occur.SHOULD);
}
}
}
@Override
public Query visit(org.elasticsearch.geo.geometry.Line line) {
validateIsGeoShapeFieldType();
return LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), new Line(line.getLats(), line.getLons()));
}
@Override
public Query visit(LinearRing ring) {
throw new QueryShardException(context, "Field [" + fieldName + "] found and unsupported shape LinearRing");
}
@Override
public Query visit(MultiLine multiLine) {
validateIsGeoShapeFieldType();
Line[] lines = new Line[multiLine.size()];
for (int i=0; i<multiLine.size(); i++) {
lines[i] = new Line(multiLine.get(i).getLats(), multiLine.get(i).getLons());
}
return LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), lines);
}
@Override
public Query visit(MultiPoint multiPoint) {
throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.MULTIPOINT +
" queries");
}
@Override
public Query visit(MultiPolygon multiPolygon) {
Polygon[] polygons = new Polygon[multiPolygon.size()];
for (int i=0; i<multiPolygon.size(); i++) {
polygons[i] = toLucenePolygon(multiPolygon.get(i));
}
return LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), polygons);
}
@Override
public Query visit(Point point) {
validateIsGeoShapeFieldType();
return LatLonShape.newBoxQuery(fieldName, relation.getLuceneRelation(),
point.getLat(), point.getLat(), point.getLon(), point.getLon());
}
@Override
public Query visit(org.elasticsearch.geo.geometry.Polygon polygon) {
return LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), toLucenePolygon(polygon));
}
@Override
public Query visit(org.elasticsearch.geo.geometry.Rectangle r) {
return LatLonShape.newBoxQuery(fieldName(), relation.getLuceneRelation(),
r.getMinLat(), r.getMaxLat(), r.getMinLon(), r.getMaxLon());
}
private void validateIsGeoShapeFieldType() {
if (fieldType instanceof GeoShapeFieldMapper.GeoShapeFieldType == false) {
throw new QueryShardException(context, "Expected " + GeoShapeFieldMapper.CONTENT_TYPE
+ " field type for Field [" + fieldName + "] but found " + fieldType.typeName());
}
}
final AbstractGeometryFieldMapper.AbstractGeometryFieldType ft = (AbstractGeometryFieldMapper.AbstractGeometryFieldType) fieldType;
return new ConstantScoreQuery(ft.geometryQueryBuilder().process(shape, fieldName, strategy, relation, context));
}
@Override

View File

@ -0,0 +1,197 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.query;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
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.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.geo.geometry.Circle;
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.GeometryCollection;
import org.elasticsearch.geo.geometry.GeometryVisitor;
import org.elasticsearch.geo.geometry.LinearRing;
import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.geo.geometry.Rectangle;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Shape;
import java.util.ArrayList;
import java.util.List;
public class LegacyGeoShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
private AbstractGeometryFieldMapper.AbstractGeometryFieldType ft;
public LegacyGeoShapeQueryProcessor(AbstractGeometryFieldMapper.AbstractGeometryFieldType ft) {
this.ft = ft;
}
@Override
public Query process(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation, QueryShardContext context) {
LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (LegacyGeoShapeFieldMapper.GeoShapeFieldType) ft;
SpatialStrategy spatialStrategy = shapeFieldType.strategy();
if (strategy != null) {
spatialStrategy = 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(shape, ShapeRelation.INTERSECTS));
bool.add(exists, BooleanClause.Occur.MUST);
bool.add(intersects, BooleanClause.Occur.MUST_NOT);
return bool.build();
} else {
return prefixTreeStrategy.makeQuery(getArgs(shape, relation));
}
}
public static SpatialArgs getArgs(Geometry shape, ShapeRelation relation) {
switch (relation) {
case DISJOINT:
return new SpatialArgs(SpatialOperation.IsDisjointTo, buildS4J(shape));
case INTERSECTS:
return new SpatialArgs(SpatialOperation.Intersects, buildS4J(shape));
case WITHIN:
return new SpatialArgs(SpatialOperation.IsWithin, buildS4J(shape));
case CONTAINS:
return new SpatialArgs(SpatialOperation.Contains, buildS4J(shape));
default:
throw new IllegalArgumentException("invalid relation [" + relation + "]");
}
}
/**
* Builds JTS shape from a geometry
* <p>
* This method is needed to handle legacy indices and will be removed when we no longer need to build JTS shapes
*/
private static Shape buildS4J(Geometry geometry) {
return geometryToShapeBuilder(geometry).buildS4J();
}
public static ShapeBuilder<?, ?, ?> geometryToShapeBuilder(Geometry geometry) {
ShapeBuilder<?, ?, ?> shapeBuilder = geometry.visit(new GeometryVisitor<ShapeBuilder<?, ?, ?>, RuntimeException>() {
@Override
public ShapeBuilder<?, ?, ?> visit(Circle circle) {
throw new UnsupportedOperationException("circle is not supported");
}
@Override
public ShapeBuilder<?, ?, ?> visit(GeometryCollection<?> collection) {
GeometryCollectionBuilder shapes = new GeometryCollectionBuilder();
for (Geometry geometry : collection) {
shapes.shape(geometry.visit(this));
}
return shapes;
}
@Override
public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Line line) {
List<Coordinate> coordinates = new ArrayList<>();
for (int i = 0; i < line.length(); i++) {
coordinates.add(new Coordinate(line.getLon(i), line.getLat(i), line.getAlt(i)));
}
return new LineStringBuilder(coordinates);
}
@Override
public ShapeBuilder<?, ?, ?> visit(LinearRing ring) {
throw new UnsupportedOperationException("circle is not supported");
}
@Override
public ShapeBuilder<?, ?, ?> visit(MultiLine multiLine) {
MultiLineStringBuilder lines = new MultiLineStringBuilder();
for (int i = 0; i < multiLine.size(); i++) {
lines.linestring((LineStringBuilder) visit(multiLine.get(i)));
}
return lines;
}
@Override
public ShapeBuilder<?, ?, ?> visit(MultiPoint multiPoint) {
List<Coordinate> coordinates = new ArrayList<>();
for (int i = 0; i < multiPoint.size(); i++) {
Point p = multiPoint.get(i);
coordinates.add(new Coordinate(p.getLon(), p.getLat(), p.getAlt()));
}
return new MultiPointBuilder(coordinates);
}
@Override
public ShapeBuilder<?, ?, ?> visit(MultiPolygon multiPolygon) {
MultiPolygonBuilder polygons = new MultiPolygonBuilder();
for (int i = 0; i < multiPolygon.size(); i++) {
polygons.polygon((PolygonBuilder) visit(multiPolygon.get(i)));
}
return polygons;
}
@Override
public ShapeBuilder<?, ?, ?> visit(Point point) {
return new PointBuilder(point.getLon(), point.getLat());
}
@Override
public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Polygon polygon) {
PolygonBuilder polygonBuilder =
new PolygonBuilder((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getPolygon()),
ShapeBuilder.Orientation.RIGHT, false);
for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
polygonBuilder.hole((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getHole(i)));
}
return polygonBuilder;
}
@Override
public ShapeBuilder<?, ?, ?> visit(Rectangle rectangle) {
return new EnvelopeBuilder(new Coordinate(rectangle.getMinLon(), rectangle.getMaxLat()),
new Coordinate(rectangle.getMaxLon(), rectangle.getMinLat()));
}
});
return shapeBuilder;
}
}

View File

@ -0,0 +1,171 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.query;
import org.apache.lucene.document.LatLonShape;
import org.apache.lucene.geo.Line;
import org.apache.lucene.geo.Polygon;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.geo.geometry.Circle;
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.GeometryCollection;
import org.elasticsearch.geo.geometry.GeometryVisitor;
import org.elasticsearch.geo.geometry.LinearRing;
import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.MappedFieldType;
import static org.elasticsearch.index.mapper.GeoShapeFieldMapper.toLucenePolygon;
public class VectorGeoShapeQueryProcessor implements AbstractGeometryFieldMapper.QueryProcessor {
@Override
public Query process(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation, QueryShardContext context) {
// 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(shape, fieldName, relation, context);
}
protected Query getVectorQueryFromShape(Geometry queryShape, String fieldName, ShapeRelation relation, QueryShardContext context) {
GeoShapeIndexer geometryIndexer = new GeoShapeIndexer(true);
Geometry processedShape = geometryIndexer.prepareForIndexing(queryShape);
if (processedShape == null) {
return new MatchNoDocsQuery();
}
return queryShape.visit(new ShapeVisitor(context, fieldName, relation));
}
private class ShapeVisitor implements GeometryVisitor<Query, RuntimeException> {
QueryShardContext context;
MappedFieldType fieldType;
String fieldName;
ShapeRelation relation;
ShapeVisitor(QueryShardContext context, String fieldName, ShapeRelation relation) {
this.context = context;
this.fieldType = context.fieldMapper(fieldName);
this.fieldName = fieldName;
this.relation = relation;
}
@Override
public Query visit(Circle circle) {
throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape Circle");
}
@Override
public Query visit(GeometryCollection<?> collection) {
BooleanQuery.Builder bqb = new BooleanQuery.Builder();
visit(bqb, collection);
return bqb.build();
}
private void visit(BooleanQuery.Builder bqb, GeometryCollection<?> collection) {
for (Geometry shape : collection) {
if (shape instanceof MultiPoint) {
// Flatten multipoints
visit(bqb, (GeometryCollection<?>) shape);
} else {
bqb.add(shape.visit(this), BooleanClause.Occur.SHOULD);
}
}
}
@Override
public Query visit(org.elasticsearch.geo.geometry.Line line) {
validateIsGeoShapeFieldType();
return LatLonShape.newLineQuery(fieldName, relation.getLuceneRelation(), new Line(line.getLats(), line.getLons()));
}
@Override
public Query visit(LinearRing ring) {
throw new QueryShardException(context, "Field [" + fieldName + "] found and unsupported shape LinearRing");
}
@Override
public Query visit(MultiLine multiLine) {
validateIsGeoShapeFieldType();
Line[] lines = new Line[multiLine.size()];
for (int i = 0; i < multiLine.size(); i++) {
lines[i] = new Line(multiLine.get(i).getLats(), multiLine.get(i).getLons());
}
return LatLonShape.newLineQuery(fieldName, relation.getLuceneRelation(), lines);
}
@Override
public Query visit(MultiPoint multiPoint) {
throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.MULTIPOINT +
" queries");
}
@Override
public Query visit(MultiPolygon multiPolygon) {
Polygon[] polygons = new Polygon[multiPolygon.size()];
for (int i = 0; i < multiPolygon.size(); i++) {
polygons[i] = toLucenePolygon(multiPolygon.get(i));
}
return LatLonShape.newPolygonQuery(fieldName, relation.getLuceneRelation(), polygons);
}
@Override
public Query visit(Point point) {
validateIsGeoShapeFieldType();
return LatLonShape.newBoxQuery(fieldName, relation.getLuceneRelation(),
point.getLat(), point.getLat(), point.getLon(), point.getLon());
}
@Override
public Query visit(org.elasticsearch.geo.geometry.Polygon polygon) {
return LatLonShape.newPolygonQuery(fieldName, relation.getLuceneRelation(), toLucenePolygon(polygon));
}
@Override
public Query visit(org.elasticsearch.geo.geometry.Rectangle r) {
return LatLonShape.newBoxQuery(fieldName, relation.getLuceneRelation(),
r.getMinLat(), r.getMaxLat(), r.getMinLon(), r.getMaxLon());
}
private void validateIsGeoShapeFieldType() {
if (fieldType instanceof GeoShapeFieldMapper.GeoShapeFieldType == false) {
throw new QueryShardException(context, "Expected " + GeoShapeFieldMapper.CONTENT_TYPE
+ " field type for Field [" + fieldName + "] but found " + fieldType.typeName());
}
}
}
}

View File

@ -31,7 +31,7 @@ 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.AbstractGeometryFieldMapper;
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;
@ -134,7 +135,7 @@ 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());
mappers.put(GeoShapeFieldMapper.CONTENT_TYPE, new AbstractGeometryFieldMapper.TypeParser());
for (MapperPlugin mapperPlugin : mapperPlugins) {
for (Map.Entry<String, Mapper.TypeParser> entry : mapperPlugin.getMappers().entrySet()) {

View File

@ -22,6 +22,7 @@ import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions;
import org.locationtech.jts.geom.Geometry;
@ -66,7 +67,7 @@ abstract class BaseGeoParsingTestCase extends ESTestCase {
} else {
GeometryParser geometryParser = new GeometryParser(true, true, true);
org.elasticsearch.geo.geometry.Geometry shape = geometryParser.parse(parser);
shape = new GeometryIndexer(true).prepareForIndexing(shape);
shape = new GeoShapeIndexer(true).prepareForIndexing(shape);
ElasticsearchGeoAssertions.assertEquals(expected, shape);
}
}

View File

@ -35,6 +35,7 @@ import org.elasticsearch.geo.geometry.GeometryCollection;
import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.index.mapper.ContentPath;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.test.VersionUtils;
@ -1425,7 +1426,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
public Geometry parse(XContentParser parser) throws IOException, ParseException {
GeometryParser geometryParser = new GeometryParser(true, true, true);
GeometryIndexer indexer = new GeometryIndexer(true);
GeoShapeIndexer indexer = new GeoShapeIndexer(true);
return indexer.prepareForIndexing(geometryParser.parse(parser));
}
}

View File

@ -47,6 +47,7 @@ import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.index.mapper.ContentPath;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.test.geo.RandomShapeGenerator;
@ -470,7 +471,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
} else {
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
assertExpected(gcb.buildS4J(), gcb, true);
assertExpected(new GeometryIndexer(true).prepareForIndexing(gcb.buildGeometry()), gcb, false);
assertExpected(new GeoShapeIndexer(true).prepareForIndexing(gcb.buildGeometry()), gcb, false);
}
}

View File

@ -30,7 +30,7 @@ import org.elasticsearch.geo.geometry.ShapeType;
import org.elasticsearch.test.ESTestCase;
import static org.elasticsearch.geo.GeometryTestUtils.randomGeometry;
import static org.elasticsearch.index.query.GeoShapeQueryBuilder.geometryToShapeBuilder;
import static org.elasticsearch.index.query.LegacyGeoShapeQueryProcessor.geometryToShapeBuilder;
public class GeometryIOTests extends ESTestCase {

View File

@ -33,6 +33,7 @@ import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.geo.geometry.Polygon;
import org.elasticsearch.geo.utils.WellKnownText;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
@ -42,7 +43,7 @@ import java.util.Collections;
public class GeometryIndexerTests extends ESTestCase {
GeometryIndexer indexer = new GeometryIndexer(true);
GeoShapeIndexer indexer = new GeoShapeIndexer(true);
private static final WellKnownText WKT = new WellKnownText(true, geometry -> {
});
@ -208,13 +209,13 @@ public class GeometryIndexerTests extends ESTestCase {
private Geometry actual(String wkt, boolean rightOrientation) throws IOException, ParseException {
Geometry shape = parseGeometry(wkt, rightOrientation);
return new GeometryIndexer(true).prepareForIndexing(shape);
return new GeoShapeIndexer(true).prepareForIndexing(shape);
}
private Geometry actual(XContentBuilder geoJson, boolean rightOrientation) throws IOException, ParseException {
Geometry shape = parseGeometry(geoJson, rightOrientation);
return new GeometryIndexer(true).prepareForIndexing(shape);
return new GeoShapeIndexer(true).prepareForIndexing(shape);
}
private Geometry parseGeometry(String wkt, boolean rightOrientation) throws IOException, ParseException {

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString;
@ -778,6 +779,6 @@ public class ShapeBuilderTests extends ESTestCase {
}
public Object buildGeometry(ShapeBuilder<?, ?, ?> builder) {
return new GeometryIndexer(true).prepareForIndexing(builder.buildGeometry());
return new GeoShapeIndexer(true).prepareForIndexing(builder.buildGeometry());
}
}

View File

@ -88,7 +88,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))
AbstractGeometryFieldMapper shapeMapper = (context.indexCreatedVersion().before(Version.V_6_6_0))
? legacyShapeBuilder.build(context)
: shapeBuilder.build(context);
FieldMapper stringMapper = (FieldMapper)stringBuilder.build(context);
@ -154,13 +154,13 @@ public class ExternalMapper extends FieldMapper {
private BinaryFieldMapper binMapper;
private BooleanFieldMapper boolMapper;
private GeoPointFieldMapper pointMapper;
private BaseGeoShapeFieldMapper shapeMapper;
private AbstractGeometryFieldMapper 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,
AbstractGeometryFieldMapper shapeMapper, FieldMapper stringMapper, Settings indexSettings,
MultiFields multiFields, CopyTo copyTo) {
super(simpleName, fieldType, new ExternalFieldType(), indexSettings, multiFields, copyTo);
this.generatedValue = generatedValue;
@ -218,7 +218,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);
AbstractGeometryFieldMapper shapeMapperUpdate = (AbstractGeometryFieldMapper) shapeMapper.updateFieldType(fullNameToFieldType);
TextFieldMapper stringMapperUpdate = (TextFieldMapper) stringMapper.updateFieldType(fullNameToFieldType);
if (update == this
&& multiFieldsUpdate == multiFields

View File

@ -280,7 +280,8 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
.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("\"orientation\":\"" +
AbstractGeometryFieldMapper.Defaults.ORIENTATION.value() + "\""));
}
}