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.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentSubParser; import org.elasticsearch.common.xcontent.XContentSubParser;
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper; import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import java.io.IOException; import java.io.IOException;
@ -42,7 +42,7 @@ import java.util.List;
* complies with geojson specification: https://tools.ietf.org/html/rfc7946 * complies with geojson specification: https://tools.ietf.org/html/rfc7946
*/ */
abstract class GeoJsonParser { abstract class GeoJsonParser {
protected static ShapeBuilder parse(XContentParser parser, BaseGeoShapeFieldMapper shapeMapper) protected static ShapeBuilder parse(XContentParser parser, AbstractGeometryFieldMapper shapeMapper)
throws IOException { throws IOException {
GeoShapeType shapeType = null; GeoShapeType shapeType = null;
DistanceUnit.Distance radius = null; DistanceUnit.Distance radius = null;
@ -50,13 +50,13 @@ abstract class GeoJsonParser {
GeometryCollectionBuilder geometryCollections = null; GeometryCollectionBuilder geometryCollections = null;
Orientation orientation = (shapeMapper == null) Orientation orientation = (shapeMapper == null)
? BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value() ? AbstractGeometryFieldMapper.Defaults.ORIENTATION.value()
: shapeMapper.orientation(); : shapeMapper.orientation();
Explicit<Boolean> coerce = (shapeMapper == null) Explicit<Boolean> coerce = (shapeMapper == null)
? BaseGeoShapeFieldMapper.Defaults.COERCE ? AbstractGeometryFieldMapper.Defaults.COERCE
: shapeMapper.coerce(); : shapeMapper.coerce();
Explicit<Boolean> ignoreZValue = (shapeMapper == null) Explicit<Boolean> ignoreZValue = (shapeMapper == null)
? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE ? AbstractGeometryFieldMapper.Defaults.IGNORE_Z_VALUE
: shapeMapper.ignoreZValue(); : shapeMapper.ignoreZValue();
String malformedException = null; String malformedException = null;
@ -208,7 +208,7 @@ abstract class GeoJsonParser {
* @return Geometry[] geometries of the GeometryCollection * @return Geometry[] geometries of the GeometryCollection
* @throws IOException Thrown if an error occurs while reading from the XContentParser * @throws IOException Thrown if an error occurs while reading from the XContentParser
*/ */
static GeometryCollectionBuilder parseGeometries(XContentParser parser, BaseGeoShapeFieldMapper mapper) throws static GeometryCollectionBuilder parseGeometries(XContentParser parser, AbstractGeometryFieldMapper mapper) throws
IOException { IOException {
if (parser.currentToken() != XContentParser.Token.START_ARRAY) { if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
throw new ElasticsearchParseException("geometries must be an array of geojson objects"); throw new ElasticsearchParseException("geometries must be an array of geojson objects");

View File

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

View File

@ -26,7 +26,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.MapXContentParser; 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.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -50,7 +50,7 @@ public interface ShapeParser {
* if the parsers current token has been <code>null</code> * if the parsers current token has been <code>null</code>
* @throws IOException if the input could not be read * @throws IOException if the input could not be read
*/ */
static ShapeBuilder parse(XContentParser parser, BaseGeoShapeFieldMapper shapeMapper) throws IOException { static ShapeBuilder parse(XContentParser parser, AbstractGeometryFieldMapper shapeMapper) throws IOException {
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) { if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
return null; return null;
} if (parser.currentToken() == XContentParser.Token.START_OBJECT) { } if (parser.currentToken() == XContentParser.Token.START_OBJECT) {

View File

@ -26,17 +26,22 @@ import org.apache.lucene.search.TermQuery;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.common.Explicit; import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.ParseField; 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;
import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation; import org.elasticsearch.common.geo.builders.ShapeBuilder.Orientation;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.DeprecatedParameters; import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.DeprecatedParameters;
import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.index.query.QueryShardException;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; 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} * Base class for {@link GeoShapeFieldMapper} and {@link LegacyGeoShapeFieldMapper}
*/ */
public abstract class BaseGeoShapeFieldMapper extends FieldMapper { public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends FieldMapper {
public static final String CONTENT_TYPE = "geo_shape";
public static class Names { public static class Names {
public static final ParseField ORIENTATION = new ParseField("orientation"); 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 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> { extends FieldMapper.Builder<T, Y> {
protected Boolean coerce; protected Boolean coerce;
protected Boolean ignoreMalformed; protected Boolean ignoreMalformed;
@ -152,7 +186,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
throw new IllegalArgumentException("name cannot be empty string"); throw new IllegalArgumentException("name cannot be empty string");
} }
BaseGeoShapeFieldType ft = (BaseGeoShapeFieldType)fieldType(); AbstractGeometryFieldType ft = (AbstractGeometryFieldType)fieldType();
ft.setOrientation(orientation().value()); 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 Orientation orientation = Defaults.ORIENTATION.value();
protected BaseGeoShapeFieldType() { protected Indexer<Parsed, Processed> geometryIndexer;
protected Parser<Parsed> geometryParser;
protected QueryProcessor geometryQueryBuilder;
protected AbstractGeometryFieldType() {
setIndexOptions(IndexOptions.DOCS); setIndexOptions(IndexOptions.DOCS);
setTokenized(false); setTokenized(false);
setStored(false); setStored(false);
@ -229,7 +269,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
setOmitNorms(true); setOmitNorms(true);
} }
protected BaseGeoShapeFieldType(BaseGeoShapeFieldType ref) { protected AbstractGeometryFieldType(AbstractGeometryFieldType ref) {
super(ref); super(ref);
this.orientation = ref.orientation; this.orientation = ref.orientation;
} }
@ -237,7 +277,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (!super.equals(o)) return false; if (!super.equals(o)) return false;
BaseGeoShapeFieldType that = (BaseGeoShapeFieldType) o; AbstractGeometryFieldType that = (AbstractGeometryFieldType) o;
return orientation == that.orientation; return orientation == that.orientation;
} }
@ -246,16 +286,6 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
return Objects.hash(super.hashCode(), orientation); 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 Orientation orientation() { return this.orientation; }
public void setOrientation(Orientation orientation) { public void setOrientation(Orientation orientation) {
@ -272,13 +302,37 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
public Query termQuery(Object value, QueryShardContext context) { public Query termQuery(Object value, QueryShardContext context) {
throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead"); throw new QueryShardException(context, "Geo fields do not support exact searching, use dedicated geo queries instead");
} }
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> coerce;
protected Explicit<Boolean> ignoreMalformed; protected Explicit<Boolean> ignoreMalformed;
protected Explicit<Boolean> ignoreZValue; protected Explicit<Boolean> ignoreZValue;
protected BaseGeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType, protected AbstractGeometryFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce, Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce,
Explicit<Boolean> ignoreZValue, Settings indexSettings, Explicit<Boolean> ignoreZValue, Settings indexSettings,
MultiFields multiFields, CopyTo copyTo) { MultiFields multiFields, CopyTo copyTo) {
@ -291,7 +345,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
@Override @Override
protected void doMerge(Mapper mergeWith) { protected void doMerge(Mapper mergeWith) {
super.doMerge(mergeWith); super.doMerge(mergeWith);
BaseGeoShapeFieldMapper gsfm = (BaseGeoShapeFieldMapper)mergeWith; AbstractGeometryFieldMapper gsfm = (AbstractGeometryFieldMapper)mergeWith;
if (gsfm.coerce.explicit()) { if (gsfm.coerce.explicit()) {
this.coerce = gsfm.coerce; this.coerce = gsfm.coerce;
} }
@ -310,7 +364,7 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
@Override @Override
protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
builder.field("type", contentType()); builder.field("type", contentType());
BaseGeoShapeFieldType ft = (BaseGeoShapeFieldType)fieldType(); AbstractGeometryFieldType ft = (AbstractGeometryFieldType)fieldType();
if (includeDefaults || ft.orientation() != Defaults.ORIENTATION.value()) { if (includeDefaults || ft.orientation() != Defaults.ORIENTATION.value()) {
builder.field(Names.ORIENTATION.getPreferredName(), ft.orientation()); builder.field(Names.ORIENTATION.getPreferredName(), ft.orientation());
} }
@ -338,11 +392,35 @@ public abstract class BaseGeoShapeFieldMapper extends FieldMapper {
} }
public Orientation orientation() { 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 @Override
protected String contentType() { public void parse(ParseContext context) throws IOException {
return CONTENT_TYPE; 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.geo.Polygon;
import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.IndexableField;
import org.elasticsearch.common.Explicit; import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.geo.GeometryIndexer;
import org.elasticsearch.common.geo.GeometryParser; import org.elasticsearch.common.geo.GeometryParser;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
@ -37,8 +36,8 @@ import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint; import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.geo.geometry.MultiPolygon; import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point; import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -62,9 +61,10 @@ import java.util.Arrays;
* <p> * <p>
* "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0)) * "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) { public Builder(String name) {
super (name, new GeoShapeFieldType(), new GeoShapeFieldType()); 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), return new GeoShapeFieldMapper(name, fieldType, defaultFieldType, ignoreMalformed(context), coerce(context),
ignoreZValue(), context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo); 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() { public GeoShapeFieldType() {
super(); super();
} }
@ -90,10 +102,17 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
public GeoShapeFieldType clone() { public GeoShapeFieldType clone() {
return new GeoShapeFieldType(this); return new GeoShapeFieldType(this);
} }
@Override
public String typeName() {
return CONTENT_TYPE;
} }
private final GeometryParser geometryParser; @Override
private final GeometryIndexer geometryIndexer; protected Indexer<Geometry, Geometry> geometryIndexer() {
return new GeoShapeIndexer(orientation == ShapeBuilder.Orientation.RIGHT);
}
}
public GeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType, public GeoShapeFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce, Explicit<Boolean> ignoreMalformed, Explicit<Boolean> coerce,
@ -101,8 +120,6 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
MultiFields multiFields, CopyTo copyTo) { MultiFields multiFields, CopyTo copyTo) {
super(simpleName, fieldType, defaultFieldType, ignoreMalformed, coerce, ignoreZValue, indexSettings, super(simpleName, fieldType, defaultFieldType, ignoreMalformed, coerce, ignoreZValue, indexSettings,
multiFields, copyTo); multiFields, copyTo);
geometryParser = new GeometryParser(orientation() == ShapeBuilder.Orientation.RIGHT, coerce().value(), ignoreZValue.value());
geometryIndexer = new GeometryIndexer(true);
} }
@Override @Override
@ -110,35 +127,9 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
return (GeoShapeFieldType) super.fieldType(); return (GeoShapeFieldType) super.fieldType();
} }
/** parsing logic for {@link LatLonShape} indexing */
@Override @Override
public void parse(ParseContext context) throws IOException { protected void indexShape(ParseContext context, Geometry luceneShape) {
try { luceneShape.visit(new LuceneGeometryIndexer(context));
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");
}
} }
private class LuceneGeometryIndexer implements GeometryVisitor<Void, RuntimeException> { private class LuceneGeometryIndexer implements GeometryVisitor<Void, RuntimeException> {
@ -232,4 +223,9 @@ public class GeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
context.doc().add(f); 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.common.collect.Tuple;
import org.elasticsearch.geo.geometry.Circle; 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 * 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; private static final double DATELINE = 180;
@ -60,7 +60,7 @@ public final class GeometryIndexer {
private final boolean orientation; private final boolean orientation;
public GeometryIndexer(boolean orientation) { public GeoShapeIndexer(boolean orientation) {
this.orientation = 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. * 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 * Array of edges will be ordered asc by the y-coordinate of the
* intersections of edges. * intersections of edges.
* *
* @param dateline * @param dateline x-coordinate of the dateline
* x-coordinate of the dateline * @param edges set of edges that may intersect with the dateline
* @param edges
* set of edges that may intersect with the dateline
* @return number of intersecting edges * @return number of intersecting edges
*/ */
protected static int intersections(double dateline, Edge[] edges) { protected static int intersections(double dateline, Edge[] edges) {
@ -698,9 +701,9 @@ public final class GeometryIndexer {
for (int i = 0; i < edges.length; i++) { for (int i = 0; i < edges.length; i++) {
if (edges[i].component >= 0) { if (edges[i].component >= 0) {
double[] partitionPoint = new double[3]; double[] partitionPoint = new double[3];
int length = component(edges[i], -(components.size()+numHoles+1), mainEdges, partitionPoint); int length = component(edges[i], -(components.size() + numHoles + 1), mainEdges, partitionPoint);
List<Point[]> component = new ArrayList<>(); 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); components.add(component);
} }
} }
@ -789,8 +792,8 @@ public final class GeometryIndexer {
private static int component(final Edge edge, final int id, final ArrayList<Edge> edges, double[] partitionPoint) { 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 // find a coordinate that is not part of the dateline
Edge any = edge; Edge any = edge;
while(any.coordinate.getLon() == +DATELINE || any.coordinate.getLon() == -DATELINE) { while (any.coordinate.getLon() == +DATELINE || any.coordinate.getLon() == -DATELINE) {
if((any = any.next) == edge) { if ((any = any.next) == edge) {
break; break;
} }
} }
@ -847,13 +850,14 @@ public final class GeometryIndexer {
prev = current; prev = current;
} }
length++; 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 * 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 * @param coordinates Array of coordinates to write the result to
* @return the coordinates parameter * @return the coordinates parameter
@ -885,7 +889,7 @@ public final class GeometryIndexer {
} }
private static Polygon buildPolygon(List<Point[]> polygon) { private static Polygon buildPolygon(List<Point[]> polygon) {
List<org.elasticsearch.geo.geometry.LinearRing> holes; List<LinearRing> holes;
Point[] shell = polygon.get(0); Point[] shell = polygon.get(0);
if (polygon.size() > 1) { if (polygon.size() > 1) {
holes = new ArrayList<>(polygon.size() - 1); holes = new ArrayList<>(polygon.size() - 1);
@ -899,7 +903,7 @@ public final class GeometryIndexer {
x[c] = normalizeLon(coords[c].getLon()); x[c] = normalizeLon(coords[c].getLon());
y[c] = normalizeLat(coords[c].getLat()); y[c] = normalizeLat(coords[c].getLat());
} }
holes.add(new org.elasticsearch.geo.geometry.LinearRing(y, x)); holes.add(new LinearRing(y, x));
} }
} else { } else {
holes = Collections.emptyList(); holes = Collections.emptyList();
@ -925,10 +929,11 @@ public final class GeometryIndexer {
for (int i = 0; i < numHoles; i++) { for (int i = 0; i < numHoles; i++) {
double[] partitionPoint = new double[3]; double[] partitionPoint = new double[3];
int length = component(holes[i], -(i+1), null, partitionPoint); // mark as visited by inverting the sign 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); points[i] = coordinates(holes[i], new Point[length + 1], partitionPoint);
} }
return points; 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.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.query.LegacyGeoShapeQueryProcessor;
import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.jts.JtsGeometry; import org.locationtech.spatial4j.shape.jts.JtsGeometry;
@ -79,7 +80,7 @@ import java.util.Objects;
* @deprecated use {@link GeoShapeFieldMapper} * @deprecated use {@link GeoShapeFieldMapper}
*/ */
@Deprecated @Deprecated
public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper { public class LegacyGeoShapeFieldMapper extends AbstractGeometryFieldMapper<ShapeBuilder<?, ?, ?>, Shape> {
public static final String CONTENT_TYPE = "geo_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 Logger logger = LogManager.getLogger(LegacyGeoShapeFieldMapper.class);
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(logger); 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; DeprecatedParameters deprecatedParameters;
@ -270,6 +272,10 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
protected void setupFieldType(BuilderContext context) { protected void setupFieldType(BuilderContext context) {
super.setupFieldType(context); super.setupFieldType(context);
fieldType().setGeometryIndexer(new LegacyGeoShapeIndexer());
fieldType().setGeometryParser(ShapeParser::parse);
fieldType().setGeometryQueryBuilder(new LegacyGeoShapeQueryProcessor(fieldType()));
// field mapper handles this at build time // field mapper handles this at build time
// but prefix tree strategies require a name, so throw a similar exception // but prefix tree strategies require a name, so throw a similar exception
if (fieldType().name().isEmpty()) { 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 String tree = DeprecatedParameters.Defaults.TREE;
private SpatialStrategy strategy = DeprecatedParameters.Defaults.STRATEGY; private SpatialStrategy strategy = DeprecatedParameters.Defaults.STRATEGY;
@ -357,6 +363,11 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
defaultDistanceErrorPct); defaultDistanceErrorPct);
} }
@Override
public String typeName() {
return CONTENT_TYPE;
}
@Override @Override
public void checkCompatibility(MappedFieldType fieldType, List<String> conflicts) { public void checkCompatibility(MappedFieldType fieldType, List<String> conflicts) {
super.checkCompatibility(fieldType, conflicts); super.checkCompatibility(fieldType, conflicts);
@ -479,23 +490,14 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
} }
@Override @Override
public void parse(ParseContext context) throws IOException { protected void indexShape(ParseContext context, Shape shape) {
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) { if (fieldType().pointsOnly() == true) {
// index configured for pointsOnly // index configured for pointsOnly
if (shape instanceof XShapeCollection && XShapeCollection.class.cast(shape).pointsOnly()) { if (shape instanceof XShapeCollection && XShapeCollection.class.cast(shape).pointsOnly()) {
// MULTIPOINT data: index each point separately // MULTIPOINT data: index each point separately
List<Shape> shapes = ((XShapeCollection) shape).getShapes(); @SuppressWarnings("unchecked") List<Shape> shapes = ((XShapeCollection) shape).getShapes();
for (Shape s : shapes) { for (Shape s : shapes) {
indexShape(context, s); doIndexShape(context, s);
} }
return; return;
} else if (shape instanceof Point == false) { } else if (shape instanceof Point == false) {
@ -504,17 +506,10 @@ public class LegacyGeoShapeFieldMapper extends BaseGeoShapeFieldMapper {
+ " was found"); + " was found");
} }
} }
indexShape(context, shape); doIndexShape(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) { private void doIndexShape(ParseContext context, Shape shape) {
List<IndexableField> fields = new ArrayList<>(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(shape))); List<IndexableField> fields = new ArrayList<>(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(shape)));
createFieldNamesField(context, fields); createFieldNamesField(context, fields);
for (IndexableField field : 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; package org.elasticsearch.index.query;
import org.apache.logging.log4j.LogManager; 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.ConstantScoreQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query; 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.Nullable;
import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.GeometryIndexer;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy; 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.builders.ShapeBuilder;
import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; 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.logging.DeprecationLogger;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geo.geometry.Circle;
import org.elasticsearch.geo.geometry.Geometry; import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.GeometryCollection; import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
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.GeoShapeFieldMapper; import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Shape;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Supplier; import java.util.function.Supplier;
import static org.elasticsearch.index.mapper.GeoShapeFieldMapper.toLucenePolygon;
/** /**
* Derived {@link AbstractGeometryQueryBuilder} that builds a lat, lon GeoShape Query * Derived {@link AbstractGeometryQueryBuilder} that builds a lat, lon GeoShape Query
*/ */
@ -217,12 +182,12 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
@Override @Override
protected List validContentTypes() { protected List validContentTypes() {
return Arrays.asList(BaseGeoShapeFieldMapper.CONTENT_TYPE); return Arrays.asList(GeoShapeFieldMapper.CONTENT_TYPE);
} }
@Override @Override
public String queryFieldType() { public String queryFieldType() {
return BaseGeoShapeFieldMapper.CONTENT_TYPE; return GeoShapeFieldMapper.CONTENT_TYPE;
} }
@Override @Override
@ -245,260 +210,13 @@ public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQ
@Override @Override
public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) { 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, throw new QueryShardException(context,
"Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]"); "Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]");
} }
final BaseGeoShapeFieldMapper.BaseGeoShapeFieldType ft = (BaseGeoShapeFieldMapper.BaseGeoShapeFieldType) fieldType; final AbstractGeometryFieldMapper.AbstractGeometryFieldType ft = (AbstractGeometryFieldMapper.AbstractGeometryFieldType) fieldType;
Query query; return new ConstantScoreQuery(ft.geometryQueryBuilder().process(shape, fieldName, strategy, relation, context));
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());
}
}
} }
@Override @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.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.engine.EngineFactory; import org.elasticsearch.index.engine.EngineFactory;
import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper; import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.BinaryFieldMapper; import org.elasticsearch.index.mapper.BinaryFieldMapper;
import org.elasticsearch.index.mapper.BooleanFieldMapper; import org.elasticsearch.index.mapper.BooleanFieldMapper;
import org.elasticsearch.index.mapper.CompletionFieldMapper; import org.elasticsearch.index.mapper.CompletionFieldMapper;
@ -39,6 +39,7 @@ import org.elasticsearch.index.mapper.DateFieldMapper;
import org.elasticsearch.index.mapper.FieldAliasMapper; import org.elasticsearch.index.mapper.FieldAliasMapper;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper; import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.GeoPointFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.IdFieldMapper;
import org.elasticsearch.index.mapper.IgnoredFieldMapper; import org.elasticsearch.index.mapper.IgnoredFieldMapper;
import org.elasticsearch.index.mapper.IndexFieldMapper; import org.elasticsearch.index.mapper.IndexFieldMapper;
@ -134,7 +135,7 @@ public class IndicesModule extends AbstractModule {
mappers.put(CompletionFieldMapper.CONTENT_TYPE, new CompletionFieldMapper.TypeParser()); mappers.put(CompletionFieldMapper.CONTENT_TYPE, new CompletionFieldMapper.TypeParser());
mappers.put(FieldAliasMapper.CONTENT_TYPE, new FieldAliasMapper.TypeParser()); mappers.put(FieldAliasMapper.CONTENT_TYPE, new FieldAliasMapper.TypeParser());
mappers.put(GeoPointFieldMapper.CONTENT_TYPE, new GeoPointFieldMapper.TypeParser()); mappers.put(GeoPointFieldMapper.CONTENT_TYPE, new GeoPointFieldMapper.TypeParser());
mappers.put(BaseGeoShapeFieldMapper.CONTENT_TYPE, new BaseGeoShapeFieldMapper.TypeParser()); mappers.put(GeoShapeFieldMapper.CONTENT_TYPE, new AbstractGeometryFieldMapper.TypeParser());
for (MapperPlugin mapperPlugin : mapperPlugins) { for (MapperPlugin mapperPlugin : mapperPlugins) {
for (Map.Entry<String, Mapper.TypeParser> entry : mapperPlugin.getMappers().entrySet()) { for (Map.Entry<String, Mapper.TypeParser> entry : mapperPlugin.getMappers().entrySet()) {

View File

@ -22,6 +22,7 @@ import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geo.utils.GeographyValidator; import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions; import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
@ -66,7 +67,7 @@ abstract class BaseGeoParsingTestCase extends ESTestCase {
} else { } else {
GeometryParser geometryParser = new GeometryParser(true, true, true); GeometryParser geometryParser = new GeometryParser(true, true, true);
org.elasticsearch.geo.geometry.Geometry shape = geometryParser.parse(parser); 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); 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.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint; import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.index.mapper.ContentPath; import org.elasticsearch.index.mapper.ContentPath;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper; import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.VersionUtils;
@ -1425,7 +1426,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
public Geometry parse(XContentParser parser) throws IOException, ParseException { public Geometry parse(XContentParser parser) throws IOException, ParseException {
GeometryParser geometryParser = new GeometryParser(true, true, true); GeometryParser geometryParser = new GeometryParser(true, true, true);
GeometryIndexer indexer = new GeometryIndexer(true); GeoShapeIndexer indexer = new GeoShapeIndexer(true);
return indexer.prepareForIndexing(geometryParser.parse(parser)); 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.geo.geometry.MultiPoint;
import org.elasticsearch.index.mapper.ContentPath; import org.elasticsearch.index.mapper.ContentPath;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper; import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper; import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.test.geo.RandomShapeGenerator; import org.elasticsearch.test.geo.RandomShapeGenerator;
@ -470,7 +471,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
} else { } else {
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random()); GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
assertExpected(gcb.buildS4J(), gcb, true); 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 org.elasticsearch.test.ESTestCase;
import static org.elasticsearch.geo.GeometryTestUtils.randomGeometry; 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 { 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.Point;
import org.elasticsearch.geo.geometry.Polygon; import org.elasticsearch.geo.geometry.Polygon;
import org.elasticsearch.geo.utils.WellKnownText; import org.elasticsearch.geo.utils.WellKnownText;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
@ -42,7 +43,7 @@ import java.util.Collections;
public class GeometryIndexerTests extends ESTestCase { public class GeometryIndexerTests extends ESTestCase {
GeometryIndexer indexer = new GeometryIndexer(true); GeoShapeIndexer indexer = new GeoShapeIndexer(true);
private static final WellKnownText WKT = new WellKnownText(true, geometry -> { 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 { private Geometry actual(String wkt, boolean rightOrientation) throws IOException, ParseException {
Geometry shape = parseGeometry(wkt, rightOrientation); 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 { private Geometry actual(XContentBuilder geoJson, boolean rightOrientation) throws IOException, ParseException {
Geometry shape = parseGeometry(geoJson, rightOrientation); 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 { 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.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder; import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LineString;
@ -778,6 +779,6 @@ public class ShapeBuilderTests extends ESTestCase {
} }
public Object buildGeometry(ShapeBuilder<?, ?, ?> builder) { 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); BinaryFieldMapper binMapper = binBuilder.build(context);
BooleanFieldMapper boolMapper = boolBuilder.build(context); BooleanFieldMapper boolMapper = boolBuilder.build(context);
GeoPointFieldMapper pointMapper = latLonPointBuilder.build(context); GeoPointFieldMapper pointMapper = latLonPointBuilder.build(context);
BaseGeoShapeFieldMapper shapeMapper = (context.indexCreatedVersion().before(Version.V_6_6_0)) AbstractGeometryFieldMapper shapeMapper = (context.indexCreatedVersion().before(Version.V_6_6_0))
? legacyShapeBuilder.build(context) ? legacyShapeBuilder.build(context)
: shapeBuilder.build(context); : shapeBuilder.build(context);
FieldMapper stringMapper = (FieldMapper)stringBuilder.build(context); FieldMapper stringMapper = (FieldMapper)stringBuilder.build(context);
@ -154,13 +154,13 @@ public class ExternalMapper extends FieldMapper {
private BinaryFieldMapper binMapper; private BinaryFieldMapper binMapper;
private BooleanFieldMapper boolMapper; private BooleanFieldMapper boolMapper;
private GeoPointFieldMapper pointMapper; private GeoPointFieldMapper pointMapper;
private BaseGeoShapeFieldMapper shapeMapper; private AbstractGeometryFieldMapper shapeMapper;
private FieldMapper stringMapper; private FieldMapper stringMapper;
public ExternalMapper(String simpleName, MappedFieldType fieldType, public ExternalMapper(String simpleName, MappedFieldType fieldType,
String generatedValue, String mapperName, String generatedValue, String mapperName,
BinaryFieldMapper binMapper, BooleanFieldMapper boolMapper, GeoPointFieldMapper pointMapper, BinaryFieldMapper binMapper, BooleanFieldMapper boolMapper, GeoPointFieldMapper pointMapper,
BaseGeoShapeFieldMapper shapeMapper, FieldMapper stringMapper, Settings indexSettings, AbstractGeometryFieldMapper shapeMapper, FieldMapper stringMapper, Settings indexSettings,
MultiFields multiFields, CopyTo copyTo) { MultiFields multiFields, CopyTo copyTo) {
super(simpleName, fieldType, new ExternalFieldType(), indexSettings, multiFields, copyTo); super(simpleName, fieldType, new ExternalFieldType(), indexSettings, multiFields, copyTo);
this.generatedValue = generatedValue; this.generatedValue = generatedValue;
@ -218,7 +218,7 @@ public class ExternalMapper extends FieldMapper {
BinaryFieldMapper binMapperUpdate = (BinaryFieldMapper) binMapper.updateFieldType(fullNameToFieldType); BinaryFieldMapper binMapperUpdate = (BinaryFieldMapper) binMapper.updateFieldType(fullNameToFieldType);
BooleanFieldMapper boolMapperUpdate = (BooleanFieldMapper) boolMapper.updateFieldType(fullNameToFieldType); BooleanFieldMapper boolMapperUpdate = (BooleanFieldMapper) boolMapper.updateFieldType(fullNameToFieldType);
GeoPointFieldMapper pointMapperUpdate = (GeoPointFieldMapper) pointMapper.updateFieldType(fullNameToFieldType); GeoPointFieldMapper pointMapperUpdate = (GeoPointFieldMapper) pointMapper.updateFieldType(fullNameToFieldType);
BaseGeoShapeFieldMapper shapeMapperUpdate = (BaseGeoShapeFieldMapper) shapeMapper.updateFieldType(fullNameToFieldType); AbstractGeometryFieldMapper shapeMapperUpdate = (AbstractGeometryFieldMapper) shapeMapper.updateFieldType(fullNameToFieldType);
TextFieldMapper stringMapperUpdate = (TextFieldMapper) stringMapper.updateFieldType(fullNameToFieldType); TextFieldMapper stringMapperUpdate = (TextFieldMapper) stringMapper.updateFieldType(fullNameToFieldType);
if (update == this if (update == this
&& multiFieldsUpdate == multiFields && multiFieldsUpdate == multiFields

View File

@ -280,7 +280,8 @@ public class GeoShapeFieldMapperTests extends ESSingleNodeTestCase {
.endObject().endObject()); .endObject().endObject());
DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping)); DocumentMapper defaultMapper = parser.parse("type1", new CompressedXContent(mapping));
String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location")); String serialized = toXContentString((GeoShapeFieldMapper) defaultMapper.mappers().getMapper("location"));
assertTrue(serialized, serialized.contains("\"orientation\":\"" + BaseGeoShapeFieldMapper.Defaults.ORIENTATION.value() + "\"")); assertTrue(serialized, serialized.contains("\"orientation\":\"" +
AbstractGeometryFieldMapper.Defaults.ORIENTATION.value() + "\""));
} }
} }