This commit refactors the following: * GeoPointFieldMapper and PointFieldMapper to AbstractPointGeometryFieldMapper derived from AbstractGeometryFieldMapper. * .setupFieldType moved up to AbstractGeometryFieldMapper * lucene indexing moved up to AbstractGeometryFieldMapper.parse * new addStoredFields, addDocValuesFields abstract methods for implementing stored field and doc values field indexing in the concrete field mappers This refactor is the next phase for setting up a framework for extending spatial field mapper functionality in x-pack.
This commit is contained in:
parent
8f2c1cda2e
commit
9b64149ad2
|
@ -41,12 +41,12 @@ import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
|
|
||||||
public final class GeoPoint implements ToXContentFragment {
|
public class GeoPoint implements ToXContentFragment {
|
||||||
|
|
||||||
private double lat;
|
protected double lat;
|
||||||
private double lon;
|
protected double lon;
|
||||||
|
|
||||||
public GeoPoint() {
|
public GeoPoint() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -373,12 +373,25 @@ public class GeoUtils {
|
||||||
* Array: two or more elements, the first element is longitude, the second is latitude, the rest is ignored if ignoreZValue is true
|
* Array: two or more elements, the first element is longitude, the second is latitude, the rest is ignored if ignoreZValue is true
|
||||||
*/
|
*/
|
||||||
public static GeoPoint parseGeoPoint(Object value, final boolean ignoreZValue) throws ElasticsearchParseException {
|
public static GeoPoint parseGeoPoint(Object value, final boolean ignoreZValue) throws ElasticsearchParseException {
|
||||||
|
return parseGeoPoint(value, new GeoPoint(), ignoreZValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the value as a geopoint. The following types of values are supported:
|
||||||
|
* <p>
|
||||||
|
* Object: has to contain either lat and lon or geohash fields
|
||||||
|
* <p>
|
||||||
|
* String: expected to be in "latitude, longitude" format or a geohash
|
||||||
|
* <p>
|
||||||
|
* Array: two or more elements, the first element is longitude, the second is latitude, the rest is ignored if ignoreZValue is true
|
||||||
|
*/
|
||||||
|
public static GeoPoint parseGeoPoint(Object value, GeoPoint point, final boolean ignoreZValue) throws ElasticsearchParseException {
|
||||||
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE,
|
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE,
|
||||||
Collections.singletonMap("null_value", value), null)) {
|
Collections.singletonMap("null_value", value), null)) {
|
||||||
parser.nextToken(); // start object
|
parser.nextToken(); // start object
|
||||||
parser.nextToken(); // field name
|
parser.nextToken(); // field name
|
||||||
parser.nextToken(); // field value
|
parser.nextToken(); // field value
|
||||||
return parseGeoPoint(parser, new GeoPoint(), ignoreZValue);
|
return parseGeoPoint(parser, point, ignoreZValue);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new ElasticsearchParseException("error parsing geopoint", ex);
|
throw new ElasticsearchParseException("error parsing geopoint", ex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ package org.elasticsearch.common.geo;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
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.geometry.Geometry;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
@ -30,16 +29,16 @@ import java.text.ParseException;
|
||||||
/**
|
/**
|
||||||
* Geometry serializer/deserializer
|
* Geometry serializer/deserializer
|
||||||
*/
|
*/
|
||||||
public interface GeometryFormat {
|
public interface GeometryFormat<ParsedFormat> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parser JSON representation of a geometry
|
* Parser JSON representation of a geometry
|
||||||
*/
|
*/
|
||||||
Geometry fromXContent(XContentParser parser) throws IOException, ParseException;
|
ParsedFormat fromXContent(XContentParser parser) throws IOException, ParseException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes the geometry into its JSON representation
|
* Serializes the geometry into its JSON representation
|
||||||
*/
|
*/
|
||||||
XContentBuilder toXContent(Geometry geometry, XContentBuilder builder, ToXContent.Params params) throws IOException;
|
XContentBuilder toXContent(ParsedFormat geometry, XContentBuilder builder, ToXContent.Params params) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,9 +55,9 @@ public final class GeometryParser {
|
||||||
/**
|
/**
|
||||||
* Returns a geometry format object that can parse and then serialize the object back to the same format.
|
* Returns a geometry format object that can parse and then serialize the object back to the same format.
|
||||||
*/
|
*/
|
||||||
public GeometryFormat geometryFormat(XContentParser parser) {
|
public GeometryFormat<Geometry> geometryFormat(XContentParser parser) {
|
||||||
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
|
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
|
||||||
return new GeometryFormat() {
|
return new GeometryFormat<Geometry>() {
|
||||||
@Override
|
@Override
|
||||||
public Geometry fromXContent(XContentParser parser) throws IOException {
|
public Geometry fromXContent(XContentParser parser) throws IOException {
|
||||||
return null;
|
return null;
|
||||||
|
@ -74,7 +74,7 @@ public final class GeometryParser {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
} else if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||||
return new GeometryFormat() {
|
return new GeometryFormat<Geometry>() {
|
||||||
@Override
|
@Override
|
||||||
public Geometry fromXContent(XContentParser parser) throws IOException {
|
public Geometry fromXContent(XContentParser parser) throws IOException {
|
||||||
return geoJsonParser.fromXContent(parser);
|
return geoJsonParser.fromXContent(parser);
|
||||||
|
@ -90,7 +90,7 @@ public final class GeometryParser {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
|
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||||
return new GeometryFormat() {
|
return new GeometryFormat<Geometry>() {
|
||||||
@Override
|
@Override
|
||||||
public Geometry fromXContent(XContentParser parser) throws IOException, ParseException {
|
public Geometry fromXContent(XContentParser parser) throws IOException, ParseException {
|
||||||
return wellKnownTextParser.fromWKT(parser.text());
|
return wellKnownTextParser.fromWKT(parser.text());
|
||||||
|
|
|
@ -428,6 +428,10 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
|
||||||
return in.readBoolean() ? Orientation.RIGHT : Orientation.LEFT;
|
return in.readBoolean() ? Orientation.RIGHT : Orientation.LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getAsBoolean() {
|
||||||
|
return this == Orientation.RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
public static Orientation fromString(String orientation) {
|
public static Orientation fromString(String orientation) {
|
||||||
orientation = orientation.toLowerCase(Locale.ROOT);
|
orientation = orientation.toLowerCase(Locale.ROOT);
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
|
|
|
@ -26,6 +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.AbstractGeometryFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -43,14 +44,21 @@ public interface ShapeParser {
|
||||||
/**
|
/**
|
||||||
* Create a new {@link ShapeBuilder} from {@link XContent}
|
* Create a new {@link ShapeBuilder} from {@link XContent}
|
||||||
* @param parser parser to read the GeoShape from
|
* @param parser parser to read the GeoShape from
|
||||||
* @param shapeMapper document field mapper reference required for spatial parameters relevant
|
* @param geometryMapper document field mapper reference required for spatial parameters relevant
|
||||||
* to the shape construction process (e.g., orientation)
|
* to the shape construction process (e.g., orientation)
|
||||||
* todo: refactor to place build specific parameters in the SpatialContext
|
* todo: refactor to place build specific parameters in the SpatialContext
|
||||||
* @return {@link ShapeBuilder} read from the parser or null
|
* @return {@link ShapeBuilder} read from the parser or null
|
||||||
* 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, AbstractShapeGeometryFieldMapper shapeMapper) throws IOException {
|
static ShapeBuilder parse(XContentParser parser, AbstractGeometryFieldMapper geometryMapper) throws IOException {
|
||||||
|
AbstractShapeGeometryFieldMapper shapeMapper = null;
|
||||||
|
if (geometryMapper != null) {
|
||||||
|
if (geometryMapper instanceof AbstractShapeGeometryFieldMapper == false) {
|
||||||
|
throw new IllegalArgumentException("geometry must be a shape type");
|
||||||
|
}
|
||||||
|
shapeMapper = (AbstractShapeGeometryFieldMapper) geometryMapper;
|
||||||
|
}
|
||||||
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) {
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
package org.elasticsearch.index.mapper;
|
package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
import org.apache.lucene.index.IndexOptions;
|
import org.apache.lucene.index.IndexOptions;
|
||||||
|
import org.apache.lucene.index.IndexableField;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
|
import org.apache.lucene.search.DocValuesFieldExistsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.TermQuery;
|
import org.apache.lucene.search.TermQuery;
|
||||||
import org.elasticsearch.common.Explicit;
|
import org.elasticsearch.common.Explicit;
|
||||||
|
@ -29,20 +31,24 @@ import org.elasticsearch.common.geo.SpatialStrategy;
|
||||||
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.geometry.Geometry;
|
import org.elasticsearch.geometry.Geometry;
|
||||||
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.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base field mapper class for all spatial field types
|
* Base field mapper class for all spatial field types
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends FieldMapper {
|
||||||
|
|
||||||
public static class Names {
|
public static class Names {
|
||||||
public static final ParseField IGNORE_MALFORMED = new ParseField("ignore_malformed");
|
public static final ParseField IGNORE_MALFORMED = new ParseField("ignore_malformed");
|
||||||
|
@ -54,7 +60,23 @@ public abstract class AbstractGeometryFieldMapper 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 AbstractGeometryFieldMapper>
|
/**
|
||||||
|
* Interface representing an preprocessor in geometry indexing pipeline
|
||||||
|
*/
|
||||||
|
public interface Indexer<Parsed, Processed> {
|
||||||
|
Processed prepareForIndexing(Parsed geometry);
|
||||||
|
Class<Processed> processedClass();
|
||||||
|
List<IndexableField> indexShape(ParseContext context, Processed shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interface representing parser in geometry indexing pipeline
|
||||||
|
*/
|
||||||
|
public interface Parser<Parsed> {
|
||||||
|
Parsed parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class Builder<T extends Builder, Y extends AbstractGeometryFieldMapper, FT extends AbstractGeometryFieldType>
|
||||||
extends FieldMapper.Builder<T, Y> {
|
extends FieldMapper.Builder<T, Y> {
|
||||||
protected Boolean ignoreMalformed;
|
protected Boolean ignoreMalformed;
|
||||||
protected Boolean ignoreZValue;
|
protected Boolean ignoreZValue;
|
||||||
|
@ -89,7 +111,7 @@ public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
||||||
if (ignoreMalformed != null) {
|
if (ignoreMalformed != null) {
|
||||||
return new Explicit<>(ignoreMalformed, true);
|
return new Explicit<>(ignoreMalformed, true);
|
||||||
}
|
}
|
||||||
return AbstractShapeGeometryFieldMapper.Defaults.IGNORE_MALFORMED;
|
return Defaults.IGNORE_MALFORMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Explicit<Boolean> ignoreZValue(BuilderContext context) {
|
protected Explicit<Boolean> ignoreZValue(BuilderContext context) {
|
||||||
|
@ -103,7 +125,7 @@ public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
||||||
if (ignoreZValue != null) {
|
if (ignoreZValue != null) {
|
||||||
return new Explicit<>(ignoreZValue, true);
|
return new Explicit<>(ignoreZValue, true);
|
||||||
}
|
}
|
||||||
return AbstractShapeGeometryFieldMapper.Defaults.IGNORE_Z_VALUE;
|
return Defaults.IGNORE_Z_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder ignoreZValue(final boolean ignoreZValue) {
|
public Builder ignoreZValue(final boolean ignoreZValue) {
|
||||||
|
@ -120,7 +142,20 @@ public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
||||||
if (name().isEmpty()) {
|
if (name().isEmpty()) {
|
||||||
throw new IllegalArgumentException("name cannot be empty string");
|
throw new IllegalArgumentException("name cannot be empty string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setGeometryParser();
|
||||||
|
setGeometryIndexer(fieldType());
|
||||||
|
setGeometryQueryBuilder(fieldType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FT fieldType() {
|
||||||
|
return (FT)fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void setGeometryParser();
|
||||||
|
protected abstract void setGeometryIndexer(FT fieldType);
|
||||||
|
protected abstract void setGeometryQueryBuilder(FT fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class TypeParser<T extends Builder> implements Mapper.TypeParser {
|
public abstract static class TypeParser<T extends Builder> implements Mapper.TypeParser {
|
||||||
|
@ -164,7 +199,9 @@ public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class AbstractGeometryFieldType extends MappedFieldType {
|
public abstract static class AbstractGeometryFieldType<Parsed, Processed> extends MappedFieldType {
|
||||||
|
protected Indexer<Parsed, Processed> geometryIndexer;
|
||||||
|
protected Parser<Parsed> geometryParser;
|
||||||
protected QueryProcessor geometryQueryBuilder;
|
protected QueryProcessor geometryQueryBuilder;
|
||||||
|
|
||||||
protected AbstractGeometryFieldType() {
|
protected AbstractGeometryFieldType() {
|
||||||
|
@ -183,6 +220,22 @@ public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
||||||
this.geometryQueryBuilder = geometryQueryBuilder;
|
this.geometryQueryBuilder = geometryQueryBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setGeometryIndexer(Indexer<Parsed, Processed> geometryIndexer) {
|
||||||
|
this.geometryIndexer = geometryIndexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Indexer<Parsed, Processed> geometryIndexer() {
|
||||||
|
return geometryIndexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeometryParser(Parser geometryParser) {
|
||||||
|
this.geometryParser = geometryParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Parser<Parsed> geometryParser() {
|
||||||
|
return geometryParser;
|
||||||
|
}
|
||||||
|
|
||||||
public QueryProcessor geometryQueryBuilder() {
|
public QueryProcessor geometryQueryBuilder() {
|
||||||
return geometryQueryBuilder;
|
return geometryQueryBuilder;
|
||||||
}
|
}
|
||||||
|
@ -202,8 +255,12 @@ public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Query existsQuery(QueryShardContext context) {
|
public Query existsQuery(QueryShardContext context) {
|
||||||
|
if (hasDocValues()) {
|
||||||
|
return new DocValuesFieldExistsQuery(name());
|
||||||
|
} else {
|
||||||
return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name()));
|
return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Query termQuery(Object value, QueryShardContext context) {
|
public Query termQuery(Object value, QueryShardContext context) {
|
||||||
|
@ -237,11 +294,74 @@ public abstract class AbstractGeometryFieldMapper extends FieldMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractGeometryFieldType fieldType() {
|
||||||
|
return (AbstractGeometryFieldType)fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseCreateField(ParseContext context) throws IOException {
|
protected void parseCreateField(ParseContext context) throws IOException {
|
||||||
throw new UnsupportedOperationException("Parsing is implemented in parse(), this method should NEVER be called");
|
throw new UnsupportedOperationException("Parsing is implemented in parse(), this method should NEVER be called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void addStoredFields(ParseContext context, Processed geometry);
|
||||||
|
protected abstract void addDocValuesFields(String name, Processed geometry, List<IndexableField> fields, ParseContext context);
|
||||||
|
protected abstract void addMultiFields(ParseContext context, Processed geometry) throws IOException;
|
||||||
|
|
||||||
|
/** parsing logic for geometry indexing */
|
||||||
|
@Override
|
||||||
|
public void parse(ParseContext context) throws IOException {
|
||||||
|
AbstractGeometryFieldMapper.AbstractGeometryFieldType fieldType = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IndexableField> fields = new ArrayList<>();
|
||||||
|
if (fieldType.indexOptions() != IndexOptions.NONE || fieldType.hasDocValues()) {
|
||||||
|
fields.addAll(geometryIndexer.indexShape(context, shape));
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexed:
|
||||||
|
List<IndexableField> indexedFields = new ArrayList<>();
|
||||||
|
if (fieldType.indexOptions() != IndexOptions.NONE) {
|
||||||
|
indexedFields.addAll(fields);
|
||||||
|
}
|
||||||
|
// stored:
|
||||||
|
if (fieldType.stored()) {
|
||||||
|
addStoredFields(context, shape);
|
||||||
|
}
|
||||||
|
// docValues:
|
||||||
|
if (fieldType().hasDocValues()) {
|
||||||
|
addDocValuesFields(fieldType.name(), shape, fields, context);
|
||||||
|
} else if (fieldType.stored() || fieldType.indexOptions() != IndexOptions.NONE) {
|
||||||
|
createFieldNamesField(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the indexed fields to the doc:
|
||||||
|
for (IndexableField field : indexedFields) {
|
||||||
|
context.doc().add(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add multifields (e.g., used for completion suggester)
|
||||||
|
addMultiFields(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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
public void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
||||||
super.doXContentBody(builder, includeDefaults, params);
|
super.doXContentBody(builder, includeDefaults, params);
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* 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.ElasticsearchParseException;
|
||||||
|
import org.elasticsearch.common.Explicit;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.geo.GeoPoint;
|
||||||
|
import org.elasticsearch.common.geo.GeometryFormat;
|
||||||
|
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 java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.elasticsearch.index.mapper.TypeParsers.parseField;
|
||||||
|
|
||||||
|
/** Base class for for spatial fields that only support indexing points */
|
||||||
|
public abstract class AbstractPointGeometryFieldMapper<Parsed, Processed> extends AbstractGeometryFieldMapper<Parsed, Processed> {
|
||||||
|
|
||||||
|
public static class Names extends AbstractGeometryFieldMapper.Names {
|
||||||
|
public static final ParseField NULL_VALUE = new ParseField("null_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class Builder<T extends Builder, Y extends AbstractPointGeometryFieldMapper,
|
||||||
|
FT extends AbstractPointGeometryFieldType> extends AbstractGeometryFieldMapper.Builder<T, Y, FT> {
|
||||||
|
public Builder(String name, MappedFieldType fieldType, MappedFieldType defaultFieldType) {
|
||||||
|
super(name, fieldType, defaultFieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Y build(BuilderContext context, String simpleName, MappedFieldType fieldType,
|
||||||
|
MappedFieldType defaultFieldType, Settings indexSettings,
|
||||||
|
MultiFields multiFields, Explicit<Boolean> ignoreMalformed, Explicit<Boolean> ignoreZValue,
|
||||||
|
CopyTo copyTo);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Y build(BuilderContext context) {
|
||||||
|
return build(context, name, fieldType, defaultFieldType, context.indexSettings(),
|
||||||
|
multiFieldsBuilder.build(this, context), ignoreMalformed(context),
|
||||||
|
ignoreZValue(context), copyTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FT fieldType() {
|
||||||
|
return (FT)fieldType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class TypeParser<Processed, T extends Builder> extends AbstractGeometryFieldMapper.TypeParser<Builder> {
|
||||||
|
protected abstract Processed parseNullValue(Object nullValue, boolean ignoreZValue, boolean ignoreMalformed);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public T parse(String name, Map<String, Object> node, Map<String, Object> params, ParserContext parserContext) {
|
||||||
|
T builder = (T)(super.parse(name, node, params, parserContext));
|
||||||
|
parseField(builder, name, node, parserContext);
|
||||||
|
Object nullValue = null;
|
||||||
|
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
|
||||||
|
Map.Entry<String, Object> entry = iterator.next();
|
||||||
|
String propName = entry.getKey();
|
||||||
|
Object propNode = entry.getValue();
|
||||||
|
|
||||||
|
if (Names.NULL_VALUE.match(propName, LoggingDeprecationHandler.INSTANCE)) {
|
||||||
|
if (propNode == null) {
|
||||||
|
throw new MapperParsingException("Property [null_value] cannot be null.");
|
||||||
|
}
|
||||||
|
nullValue = propNode;
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullValue != null) {
|
||||||
|
builder.nullValue(parseNullValue(nullValue, (Boolean)builder.ignoreZValue().value(),
|
||||||
|
(Boolean)builder.ignoreMalformed().value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class AbstractPointGeometryFieldType<Parsed, Processed>
|
||||||
|
extends AbstractGeometryFieldType<Parsed, Processed> {
|
||||||
|
protected AbstractPointGeometryFieldType() {
|
||||||
|
super();
|
||||||
|
setHasDocValues(true);
|
||||||
|
setDimensions(2, Integer.BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractPointGeometryFieldType(AbstractPointGeometryFieldType ref) {
|
||||||
|
super(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractPointGeometryFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
|
||||||
|
Settings indexSettings, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
|
||||||
|
Explicit<Boolean> ignoreZValue, CopyTo copyTo) {
|
||||||
|
super(simpleName, fieldType, defaultFieldType, indexSettings, ignoreMalformed, ignoreZValue, multiFields, copyTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doMerge(Mapper mergeWith) {
|
||||||
|
super.doMerge(mergeWith);
|
||||||
|
AbstractPointGeometryFieldMapper gpfm = (AbstractPointGeometryFieldMapper)mergeWith;
|
||||||
|
if (gpfm.fieldType().nullValue() != null) {
|
||||||
|
this.fieldType().setNullValue(gpfm.fieldType().nullValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
||||||
|
super.doXContentBody(builder, includeDefaults, params);
|
||||||
|
if (includeDefaults || fieldType().nullValue() != null) {
|
||||||
|
builder.field(Names.NULL_VALUE.getPreferredName(), fieldType().nullValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract ParsedPoint newParsedPoint();
|
||||||
|
|
||||||
|
/** represents a Point that has been parsed by {@link PointParser} */
|
||||||
|
public interface ParsedPoint {
|
||||||
|
void validate(String fieldName);
|
||||||
|
void normalize(String fieldName);
|
||||||
|
void resetCoords(double x, double y);
|
||||||
|
default boolean isNormalizable(double coord) {
|
||||||
|
return Double.isNaN(coord) == false && Double.isInfinite(coord) == false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parsePointIgnoringMalformed(XContentParser parser, ParsedPoint point) throws IOException {
|
||||||
|
try {
|
||||||
|
if (ignoreMalformed.value() == false) {
|
||||||
|
point.validate(name());
|
||||||
|
} else {
|
||||||
|
point.normalize(name());
|
||||||
|
}
|
||||||
|
} catch (ElasticsearchParseException e) {
|
||||||
|
if (ignoreMalformed.value() == false) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A parser implementation that can parse the various point formats */
|
||||||
|
public static class PointParser<P extends ParsedPoint> implements Parser<List<P>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<P> parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException {
|
||||||
|
return geometryFormat(parser, (AbstractPointGeometryFieldMapper)mapper).fromXContent(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeometryFormat<List<P>> geometryFormat(XContentParser parser, AbstractPointGeometryFieldMapper mapper) {
|
||||||
|
if (parser.currentToken() == XContentParser.Token.START_ARRAY) {
|
||||||
|
return new GeometryFormat<List<P>>() {
|
||||||
|
@Override
|
||||||
|
public List<P> fromXContent(XContentParser parser) throws IOException {
|
||||||
|
XContentParser.Token token = parser.nextToken();
|
||||||
|
P point = (P)(mapper.newParsedPoint());
|
||||||
|
ArrayList<P> points = new ArrayList();
|
||||||
|
if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||||
|
double x = parser.doubleValue();
|
||||||
|
parser.nextToken();
|
||||||
|
double y = parser.doubleValue();
|
||||||
|
token = parser.nextToken();
|
||||||
|
if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||||
|
GeoPoint.assertZValue((Boolean)(mapper.ignoreZValue().value()), parser.doubleValue());
|
||||||
|
} else if (token != XContentParser.Token.END_ARRAY) {
|
||||||
|
throw new ElasticsearchParseException("[{}] field type does not accept > 3 dimensions",
|
||||||
|
mapper.contentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
point.resetCoords(x, y);
|
||||||
|
if ((Boolean)(mapper.ignoreMalformed().value()) == false) {
|
||||||
|
point.validate(mapper.name());
|
||||||
|
} else {
|
||||||
|
point.normalize(mapper.name());
|
||||||
|
}
|
||||||
|
points.add(point);
|
||||||
|
} else {
|
||||||
|
while (token != XContentParser.Token.END_ARRAY) {
|
||||||
|
mapper.parsePointIgnoringMalformed(parser, point);
|
||||||
|
points.add(point);
|
||||||
|
point = (P)(mapper.newParsedPoint());
|
||||||
|
token = parser.nextToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(List<P> points, XContentBuilder builder, Params params) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
|
||||||
|
return new GeometryFormat<List<P>>() {
|
||||||
|
@Override
|
||||||
|
public List<P> fromXContent(XContentParser parser) throws IOException, ParseException {
|
||||||
|
P point = null;
|
||||||
|
ArrayList<P> points = null;
|
||||||
|
if (mapper.fieldType().nullValue() != null) {
|
||||||
|
point = (P)(mapper.fieldType().nullValue());
|
||||||
|
if ((Boolean)(mapper.ignoreMalformed().value()) == false) {
|
||||||
|
point.validate(mapper.name());
|
||||||
|
} else {
|
||||||
|
point.normalize(mapper.name());
|
||||||
|
}
|
||||||
|
points = new ArrayList<>();
|
||||||
|
points.add(point);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(List<P> points, XContentBuilder builder, Params params) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return new GeometryFormat<List<P>>() {
|
||||||
|
@Override
|
||||||
|
public List<P> fromXContent(XContentParser parser) throws IOException, ParseException {
|
||||||
|
P point = (P)mapper.newParsedPoint();
|
||||||
|
mapper.parsePointIgnoringMalformed(parser, point);
|
||||||
|
ArrayList<P> points = new ArrayList();
|
||||||
|
points.add(point);
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(List<P> points, XContentBuilder builder, Params params) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.index.mapper;
|
package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
import org.apache.lucene.index.IndexableField;
|
|
||||||
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;
|
||||||
|
@ -27,21 +26,18 @@ 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.index.mapper.LegacyGeoShapeFieldMapper.DeprecatedParameters;
|
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.DeprecatedParameters;
|
||||||
|
|
||||||
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.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for {@link GeoShapeFieldMapper} and {@link LegacyGeoShapeFieldMapper}
|
* Base class for {@link GeoShapeFieldMapper} and {@link LegacyGeoShapeFieldMapper}
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extends AbstractGeometryFieldMapper {
|
public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extends AbstractGeometryFieldMapper<Parsed, Processed> {
|
||||||
|
|
||||||
public static class Names extends AbstractGeometryFieldMapper.Names {
|
public static class Names extends AbstractGeometryFieldMapper.Names {
|
||||||
public static final ParseField ORIENTATION = new ParseField("orientation");
|
public static final ParseField ORIENTATION = new ParseField("orientation");
|
||||||
|
@ -53,29 +49,8 @@ public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extend
|
||||||
public static final Explicit<Boolean> COERCE = new Explicit<>(false, false);
|
public static final Explicit<Boolean> COERCE = new Explicit<>(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public abstract static class Builder<T extends Builder, Y extends AbstractShapeGeometryFieldMapper,
|
||||||
* Interface representing an preprocessor in geo-shape indexing pipeline
|
FT extends AbstractShapeGeometryFieldType> extends AbstractGeometryFieldMapper.Builder<T, Y, FT> {
|
||||||
*/
|
|
||||||
public interface Indexer<Parsed, Processed> {
|
|
||||||
|
|
||||||
Processed prepareForIndexing(Parsed geometry);
|
|
||||||
|
|
||||||
Class<Processed> processedClass();
|
|
||||||
|
|
||||||
List<IndexableField> indexShape(ParseContext context, Processed shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* interface representing parser in geo shape indexing pipeline
|
|
||||||
*/
|
|
||||||
public interface Parser<Parsed> {
|
|
||||||
|
|
||||||
Parsed parse(XContentParser parser, AbstractShapeGeometryFieldMapper mapper) throws IOException, ParseException;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class Builder<T extends Builder, Y extends AbstractShapeGeometryFieldMapper>
|
|
||||||
extends AbstractGeometryFieldMapper.Builder<T, Y> {
|
|
||||||
protected Boolean coerce;
|
protected Boolean coerce;
|
||||||
protected Orientation orientation;
|
protected Orientation orientation;
|
||||||
|
|
||||||
|
@ -106,6 +81,13 @@ public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extend
|
||||||
return Defaults.COERCE;
|
return Defaults.COERCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Explicit<Boolean> coerce() {
|
||||||
|
if (coerce != null) {
|
||||||
|
return new Explicit<>(coerce, true);
|
||||||
|
}
|
||||||
|
return Defaults.COERCE;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder orientation(Orientation orientation) {
|
public Builder orientation(Orientation orientation) {
|
||||||
this.orientation = orientation;
|
this.orientation = orientation;
|
||||||
return this;
|
return this;
|
||||||
|
@ -124,12 +106,13 @@ public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extend
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupFieldType(BuilderContext context) {
|
protected void setGeometryParser() {
|
||||||
super.setupFieldType(context);
|
AbstractShapeGeometryFieldType ft = fieldType();
|
||||||
|
|
||||||
AbstractShapeGeometryFieldType ft = (AbstractShapeGeometryFieldType)fieldType();
|
|
||||||
ft.setOrientation(orientation().value());
|
ft.setOrientation(orientation().value());
|
||||||
|
setGeometryParser(fieldType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void setGeometryParser(FT fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static final String DEPRECATED_PARAMETERS_KEY = "deprecated_parameters";
|
protected static final String DEPRECATED_PARAMETERS_KEY = "deprecated_parameters";
|
||||||
|
@ -184,13 +167,9 @@ public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class AbstractShapeGeometryFieldType<Parsed, Processed> extends AbstractGeometryFieldType {
|
public abstract static class AbstractShapeGeometryFieldType<Parsed, Processed> extends AbstractGeometryFieldType<Parsed, Processed> {
|
||||||
protected Orientation orientation = Defaults.ORIENTATION.value();
|
protected Orientation orientation = Defaults.ORIENTATION.value();
|
||||||
|
|
||||||
protected Indexer<Parsed, Processed> geometryIndexer;
|
|
||||||
|
|
||||||
protected Parser<Parsed> geometryParser;
|
|
||||||
|
|
||||||
protected AbstractShapeGeometryFieldType() {
|
protected AbstractShapeGeometryFieldType() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -218,22 +197,6 @@ public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extend
|
||||||
checkIfFrozen();
|
checkIfFrozen();
|
||||||
this.orientation = orientation;
|
this.orientation = orientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Explicit<Boolean> coerce;
|
protected Explicit<Boolean> coerce;
|
||||||
|
@ -279,34 +242,4 @@ public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extend
|
||||||
public Orientation orientation() {
|
public Orientation orientation() {
|
||||||
return ((AbstractShapeGeometryFieldType)fieldType).orientation();
|
return ((AbstractShapeGeometryFieldType)fieldType).orientation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** parsing logic for geometry indexing */
|
|
||||||
@Override
|
|
||||||
public void parse(ParseContext context) throws IOException {
|
|
||||||
AbstractShapeGeometryFieldType fieldType = (AbstractShapeGeometryFieldType)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);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IndexableField> fields = geometryIndexer.indexShape(context, shape);
|
|
||||||
context.doc().addAll(fields);
|
|
||||||
createFieldNamesField(context);
|
|
||||||
} 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,44 +21,35 @@ package org.elasticsearch.index.mapper;
|
||||||
import org.apache.lucene.document.LatLonDocValuesField;
|
import org.apache.lucene.document.LatLonDocValuesField;
|
||||||
import org.apache.lucene.document.LatLonPoint;
|
import org.apache.lucene.document.LatLonPoint;
|
||||||
import org.apache.lucene.document.StoredField;
|
import org.apache.lucene.document.StoredField;
|
||||||
import org.apache.lucene.index.IndexOptions;
|
import org.apache.lucene.index.IndexableField;
|
||||||
import org.apache.lucene.search.DocValuesFieldExistsQuery;
|
|
||||||
import org.apache.lucene.search.Query;
|
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.Explicit;
|
import org.elasticsearch.common.Explicit;
|
||||||
import org.elasticsearch.common.ParseField;
|
|
||||||
import org.elasticsearch.common.geo.GeoPoint;
|
import org.elasticsearch.common.geo.GeoPoint;
|
||||||
import org.elasticsearch.common.geo.GeoUtils;
|
import org.elasticsearch.common.geo.GeoUtils;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
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.XContentParser;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointDVIndexFieldData;
|
import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointDVIndexFieldData;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
|
||||||
import org.elasticsearch.index.query.VectorGeoPointShapeQueryProcessor;
|
import org.elasticsearch.index.query.VectorGeoPointShapeQueryProcessor;
|
||||||
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
|
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
|
||||||
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
|
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.TypeParsers.parseField;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field Mapper for geo_point types.
|
* Field Mapper for geo_point types.
|
||||||
*
|
*
|
||||||
* Uses lucene 6 LatLonPoint encoding
|
* Uses lucene 6 LatLonPoint encoding
|
||||||
*/
|
*/
|
||||||
public class GeoPointFieldMapper extends AbstractGeometryFieldMapper implements ArrayValueMapperParser {
|
public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<? extends GeoPoint>, List<? extends GeoPoint>>
|
||||||
|
implements ArrayValueMapperParser {
|
||||||
public static final String CONTENT_TYPE = "geo_point";
|
public static final String CONTENT_TYPE = "geo_point";
|
||||||
|
|
||||||
public static class Names extends AbstractGeometryFieldMapper.Names {
|
public static class Builder extends AbstractPointGeometryFieldMapper.Builder<Builder, GeoPointFieldMapper, GeoPointFieldType> {
|
||||||
public static final ParseField NULL_VALUE = new ParseField("null_value");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder extends AbstractGeometryFieldMapper.Builder<Builder, GeoPointFieldMapper> {
|
|
||||||
public Builder(String name) {
|
public Builder(String name) {
|
||||||
super(name, new GeoPointFieldType(), new GeoPointFieldType());
|
super(name, new GeoPointFieldType(), new GeoPointFieldType());
|
||||||
builder = this;
|
builder = this;
|
||||||
|
@ -74,56 +65,31 @@ public class GeoPointFieldMapper extends AbstractGeometryFieldMapper implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupFieldType(BuilderContext context) {
|
protected void setGeometryParser() {
|
||||||
super.setupFieldType(context);
|
PointParser<ParsedGeoPoint> pointParser = new PointParser<>();
|
||||||
|
fieldType().setGeometryParser((parser, mapper) -> pointParser.parse(parser, mapper));
|
||||||
|
}
|
||||||
|
|
||||||
GeoPointFieldType fieldType = (GeoPointFieldType)fieldType();
|
@Override
|
||||||
|
protected void setGeometryIndexer(GeoPointFieldType fieldType) {
|
||||||
|
fieldType.setGeometryIndexer(new GeoPointIndexer(fieldType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setGeometryQueryBuilder(GeoPointFieldType fieldType) {
|
||||||
fieldType.setGeometryQueryBuilder(new VectorGeoPointShapeQueryProcessor());
|
fieldType.setGeometryQueryBuilder(new VectorGeoPointShapeQueryProcessor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GeoPointFieldMapper build(BuilderContext context) {
|
|
||||||
return build(context, name, fieldType, defaultFieldType, context.indexSettings(),
|
|
||||||
multiFieldsBuilder.build(this, context), ignoreMalformed(context),
|
|
||||||
ignoreZValue(context), copyTo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static class TypeParser extends AbstractPointGeometryFieldMapper.TypeParser<ParsedGeoPoint, Builder> {
|
||||||
public GeoPointFieldType fieldType() {
|
|
||||||
return (GeoPointFieldType)fieldType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TypeParser extends AbstractGeometryFieldMapper.TypeParser<Builder> {
|
|
||||||
@Override
|
@Override
|
||||||
protected Builder newBuilder(String name, Map<String, Object> params) {
|
protected Builder newBuilder(String name, Map<String, Object> params) {
|
||||||
return new GeoPointFieldMapper.Builder(name);
|
return new GeoPointFieldMapper.Builder(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected ParsedGeoPoint parseNullValue(Object nullValue, boolean ignoreZValue, boolean ignoreMalformed) {
|
||||||
public Builder parse(String name, Map<String, Object> node, Map<String, Object> params, ParserContext parserContext) {
|
ParsedGeoPoint point = new ParsedGeoPoint();
|
||||||
Builder builder = super.parse(name, node, params, parserContext);
|
GeoUtils.parseGeoPoint(nullValue, point, ignoreZValue);
|
||||||
|
|
||||||
parseField(builder, name, node, parserContext);
|
|
||||||
Object nullValue = null;
|
|
||||||
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
|
|
||||||
Map.Entry<String, Object> entry = iterator.next();
|
|
||||||
String propName = entry.getKey();
|
|
||||||
Object propNode = entry.getValue();
|
|
||||||
|
|
||||||
if (Names.NULL_VALUE.match(propName, LoggingDeprecationHandler.INSTANCE)) {
|
|
||||||
if (propNode == null) {
|
|
||||||
throw new MapperParsingException("Property [null_value] cannot be null.");
|
|
||||||
}
|
|
||||||
nullValue = propNode;
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nullValue != null) {
|
|
||||||
boolean ignoreZValue = builder.ignoreZValue().value();
|
|
||||||
boolean ignoreMalformed = builder.ignoreMalformed().value();
|
|
||||||
GeoPoint point = GeoUtils.parseGeoPoint(nullValue, ignoreZValue);
|
|
||||||
if (ignoreMalformed == false) {
|
if (ignoreMalformed == false) {
|
||||||
if (point.lat() > 90.0 || point.lat() < -90.0) {
|
if (point.lat() > 90.0 || point.lat() < -90.0) {
|
||||||
throw new IllegalArgumentException("illegal latitude value [" + point.lat() + "]");
|
throw new IllegalArgumentException("illegal latitude value [" + point.lat() + "]");
|
||||||
|
@ -134,21 +100,59 @@ public class GeoPointFieldMapper extends AbstractGeometryFieldMapper implements
|
||||||
} else {
|
} else {
|
||||||
GeoUtils.normalizePoint(point);
|
GeoUtils.normalizePoint(point);
|
||||||
}
|
}
|
||||||
builder.nullValue(point);
|
return point;
|
||||||
}
|
}
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses geopoint represented as an object or an array, ignores malformed geopoints if needed
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void parsePointIgnoringMalformed(XContentParser parser, ParsedPoint point) throws IOException {
|
||||||
|
GeoUtils.parseGeoPoint(parser, (GeoPoint)point, ignoreZValue().value());
|
||||||
|
super.parsePointIgnoringMalformed(parser, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeoPointFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
|
public GeoPointFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
|
||||||
Settings indexSettings, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
|
Settings indexSettings, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
|
||||||
Explicit<Boolean> ignoreZValue, CopyTo copyTo) {
|
Explicit<Boolean> ignoreZValue, CopyTo copyTo) {
|
||||||
super(simpleName, fieldType, defaultFieldType, indexSettings, ignoreMalformed, ignoreZValue, multiFields, copyTo);
|
super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, ignoreMalformed, ignoreZValue, copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doMerge(Mapper mergeWith) {
|
protected void addStoredFields(ParseContext context, List<? extends GeoPoint> points) {
|
||||||
super.doMerge(mergeWith);
|
for (GeoPoint point : points) {
|
||||||
|
context.doc().add(new StoredField(fieldType.name(), point.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addMultiFields(ParseContext context, List<? extends GeoPoint> points) throws IOException {
|
||||||
|
// @todo phase out geohash (which is currently used in the CompletionSuggester)
|
||||||
|
if (points.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
if (points.size() > 1) {
|
||||||
|
s.append('[');
|
||||||
|
}
|
||||||
|
s.append(points.get(0).geohash());
|
||||||
|
for (int i = 1; i < points.size(); ++i) {
|
||||||
|
s.append(',');
|
||||||
|
s.append(points.get(i).geohash());
|
||||||
|
}
|
||||||
|
if (points.size() > 1) {
|
||||||
|
s.append(']');
|
||||||
|
}
|
||||||
|
multiFields.parse(this, context.createExternalValueContext(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addDocValuesFields(String name, List<? extends GeoPoint> points, List<IndexableField> fields, ParseContext context) {
|
||||||
|
for (GeoPoint point : points) {
|
||||||
|
context.doc().add(new LatLonDocValuesField(fieldType.name(), point.lat(), point.lon()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -156,11 +160,19 @@ public class GeoPointFieldMapper extends AbstractGeometryFieldMapper implements
|
||||||
return CONTENT_TYPE;
|
return CONTENT_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GeoPointFieldType extends AbstractGeometryFieldType {
|
@Override
|
||||||
|
public GeoPointFieldType fieldType() {
|
||||||
|
return (GeoPointFieldType)fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ParsedPoint newParsedPoint() {
|
||||||
|
return new ParsedGeoPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GeoPointFieldType extends AbstractPointGeometryFieldType<List<ParsedGeoPoint>, List<ParsedGeoPoint>> {
|
||||||
public GeoPointFieldType() {
|
public GeoPointFieldType() {
|
||||||
super();
|
super();
|
||||||
setHasDocValues(true);
|
|
||||||
setDimensions(2, Integer.BYTES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GeoPointFieldType(GeoPointFieldType ref) {
|
GeoPointFieldType(GeoPointFieldType ref) {
|
||||||
|
@ -187,119 +199,93 @@ public class GeoPointFieldMapper extends AbstractGeometryFieldMapper implements
|
||||||
public ValuesSourceType getValuesSourceType() {
|
public ValuesSourceType getValuesSourceType() {
|
||||||
return CoreValuesSourceType.GEOPOINT;
|
return CoreValuesSourceType.GEOPOINT;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class ParsedGeoPoint extends GeoPoint implements ParsedPoint {
|
||||||
|
@Override
|
||||||
|
public void validate(String fieldName) {
|
||||||
|
if (lat() > 90.0 || lat() < -90.0) {
|
||||||
|
throw new IllegalArgumentException("illegal latitude value [" + lat() + "] for " + fieldName);
|
||||||
|
}
|
||||||
|
if (lon() > 180.0 || lon() < -180) {
|
||||||
|
throw new IllegalArgumentException("illegal longitude value [" + lon() + "] for " + fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Query existsQuery(QueryShardContext context) {
|
public void normalize(String name) {
|
||||||
if (hasDocValues()) {
|
if (isNormalizable(lat()) && isNormalizable(lon())) {
|
||||||
return new DocValuesFieldExistsQuery(name());
|
GeoUtils.normalizePoint(this);
|
||||||
} else {
|
|
||||||
return super.existsQuery(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void parse(ParseContext context, GeoPoint point) throws IOException {
|
|
||||||
|
|
||||||
if (ignoreMalformed.value() == false) {
|
|
||||||
if (point.lat() > 90.0 || point.lat() < -90.0) {
|
|
||||||
throw new IllegalArgumentException("illegal latitude value [" + point.lat() + "] for " + name());
|
|
||||||
}
|
|
||||||
if (point.lon() > 180.0 || point.lon() < -180) {
|
|
||||||
throw new IllegalArgumentException("illegal longitude value [" + point.lon() + "] for " + name());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isNormalizable(point.lat()) && isNormalizable(point.lon())) {
|
|
||||||
GeoUtils.normalizePoint(point);
|
|
||||||
} else {
|
} else {
|
||||||
throw new ElasticsearchParseException("cannot normalize the point - not a number");
|
throw new ElasticsearchParseException("cannot normalize the point - not a number");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fieldType().indexOptions() != IndexOptions.NONE) {
|
|
||||||
context.doc().add(new LatLonPoint(fieldType().name(), point.lat(), point.lon()));
|
|
||||||
}
|
|
||||||
if (fieldType().stored()) {
|
|
||||||
context.doc().add(new StoredField(fieldType().name(), point.toString()));
|
|
||||||
}
|
|
||||||
if (fieldType.hasDocValues()) {
|
|
||||||
context.doc().add(new LatLonDocValuesField(fieldType().name(), point.lat(), point.lon()));
|
|
||||||
} else if (fieldType().stored() || fieldType().indexOptions() != IndexOptions.NONE) {
|
|
||||||
createFieldNamesField(context);
|
|
||||||
}
|
|
||||||
// if the mapping contains multifields then use the geohash string
|
|
||||||
if (multiFields.iterator().hasNext()) {
|
|
||||||
multiFields.parse(this, context.createExternalValueContext(point.geohash()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parse(ParseContext context) throws IOException {
|
public boolean isNormalizable(double coord) {
|
||||||
context.path().add(simpleName());
|
|
||||||
|
|
||||||
try {
|
|
||||||
GeoPoint sparse = context.parseExternalValue(GeoPoint.class);
|
|
||||||
|
|
||||||
if (sparse != null) {
|
|
||||||
parse(context, sparse);
|
|
||||||
} else {
|
|
||||||
sparse = new GeoPoint();
|
|
||||||
XContentParser.Token token = context.parser().currentToken();
|
|
||||||
if (token == XContentParser.Token.START_ARRAY) {
|
|
||||||
token = context.parser().nextToken();
|
|
||||||
if (token == XContentParser.Token.VALUE_NUMBER) {
|
|
||||||
double lon = context.parser().doubleValue();
|
|
||||||
context.parser().nextToken();
|
|
||||||
double lat = context.parser().doubleValue();
|
|
||||||
token = context.parser().nextToken();
|
|
||||||
if (token == XContentParser.Token.VALUE_NUMBER) {
|
|
||||||
GeoPoint.assertZValue(ignoreZValue.value(), context.parser().doubleValue());
|
|
||||||
} else if (token != XContentParser.Token.END_ARRAY) {
|
|
||||||
throw new ElasticsearchParseException("[{}] field type does not accept > 3 dimensions", CONTENT_TYPE);
|
|
||||||
}
|
|
||||||
parse(context, sparse.reset(lat, lon));
|
|
||||||
} else {
|
|
||||||
while (token != XContentParser.Token.END_ARRAY) {
|
|
||||||
parseGeoPointIgnoringMalformed(context, sparse);
|
|
||||||
token = context.parser().nextToken();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (token == XContentParser.Token.VALUE_NULL) {
|
|
||||||
if (fieldType.nullValue() != null) {
|
|
||||||
parse(context, (GeoPoint) fieldType.nullValue());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parseGeoPointIgnoringMalformed(context, sparse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new MapperParsingException("failed to parse field [{}] of type [{}]", ex, fieldType().name(), fieldType().typeName());
|
|
||||||
}
|
|
||||||
|
|
||||||
context.path().remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses geopoint represented as an object or an array, ignores malformed geopoints if needed
|
|
||||||
*/
|
|
||||||
private void parseGeoPointIgnoringMalformed(ParseContext context, GeoPoint sparse) throws IOException {
|
|
||||||
try {
|
|
||||||
parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse, ignoreZValue.value()));
|
|
||||||
} catch (ElasticsearchParseException e) {
|
|
||||||
if (ignoreMalformed.value() == false) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
context.addIgnoredField(fieldType.name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
|
||||||
super.doXContentBody(builder, includeDefaults, params);
|
|
||||||
if (includeDefaults || fieldType().nullValue() != null) {
|
|
||||||
builder.field(Names.NULL_VALUE.getPreferredName(), fieldType().nullValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isNormalizable(double coord) {
|
|
||||||
return Double.isNaN(coord) == false && Double.isInfinite(coord) == false;
|
return Double.isNaN(coord) == false && Double.isInfinite(coord) == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetCoords(double x, double y) {
|
||||||
|
this.reset(y, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
double oLat;
|
||||||
|
double oLon;
|
||||||
|
if (other instanceof GeoPoint) {
|
||||||
|
GeoPoint o = (GeoPoint)other;
|
||||||
|
oLat = o.lat();
|
||||||
|
oLon = o.lon();
|
||||||
|
} else if (other instanceof ParsedGeoPoint == false) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
ParsedGeoPoint o = (ParsedGeoPoint)other;
|
||||||
|
oLat = o.lat();
|
||||||
|
oLon = o.lon();
|
||||||
|
}
|
||||||
|
if (Double.compare(oLat, lat) != 0) return false;
|
||||||
|
if (Double.compare(oLon, lon) != 0) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class GeoPointIndexer implements Indexer<List<ParsedGeoPoint>, List<ParsedGeoPoint>> {
|
||||||
|
|
||||||
|
protected final GeoPointFieldType fieldType;
|
||||||
|
|
||||||
|
GeoPointIndexer(GeoPointFieldType fieldType) {
|
||||||
|
this.fieldType = fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ParsedGeoPoint> prepareForIndexing(List<ParsedGeoPoint> geoPoints) {
|
||||||
|
if (geoPoints == null || geoPoints.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return geoPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<List<ParsedGeoPoint>> processedClass() {
|
||||||
|
return (Class<List<ParsedGeoPoint>>)(Object)List.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IndexableField> indexShape(ParseContext context, List<ParsedGeoPoint> points) {
|
||||||
|
ArrayList<IndexableField> fields = new ArrayList<>(points.size());
|
||||||
|
for (GeoPoint point : points) {
|
||||||
|
fields.add(new LatLonPoint(fieldType.name(), point.lat(), point.lon()));
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.geometry.Geometry;
|
import org.elasticsearch.geometry.Geometry;
|
||||||
import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
|
import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +53,7 @@ public class GeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geomet
|
||||||
public static final String CONTENT_TYPE = "geo_shape";
|
public static final String CONTENT_TYPE = "geo_shape";
|
||||||
|
|
||||||
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
||||||
GeoShapeFieldMapper> {
|
GeoShapeFieldMapper, GeoShapeFieldType> {
|
||||||
public Builder(String name) {
|
public Builder(String name) {
|
||||||
super (name, new GeoShapeFieldType(), new GeoShapeFieldType());
|
super (name, new GeoShapeFieldType(), new GeoShapeFieldType());
|
||||||
}
|
}
|
||||||
|
@ -65,16 +66,20 @@ public class GeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geomet
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupFieldType(BuilderContext context) {
|
protected void setGeometryParser(GeoShapeFieldType ft) {
|
||||||
super.setupFieldType(context);
|
// @todo check coerce
|
||||||
|
GeometryParser geometryParser = new GeometryParser(ft.orientation.getAsBoolean(), coerce().value(),
|
||||||
|
ignoreZValue().value());
|
||||||
|
ft.setGeometryParser( (parser, mapper) -> geometryParser.parse(parser));
|
||||||
|
}
|
||||||
|
|
||||||
GeoShapeFieldType fieldType = (GeoShapeFieldType)fieldType();
|
@Override
|
||||||
boolean orientation = fieldType.orientation == ShapeBuilder.Orientation.RIGHT;
|
protected void setGeometryIndexer(GeoShapeFieldType fieldType) {
|
||||||
|
fieldType.setGeometryIndexer(new GeoShapeIndexer(fieldType.orientation.getAsBoolean(), fieldType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
GeometryParser geometryParser = new GeometryParser(orientation, coerce(context).value(), ignoreZValue().value());
|
@Override
|
||||||
|
protected void setGeometryQueryBuilder(GeoShapeFieldType fieldType) {
|
||||||
fieldType.setGeometryIndexer(new GeoShapeIndexer(orientation, fieldType.name()));
|
|
||||||
fieldType.setGeometryParser( (parser, mapper) -> geometryParser.parse(parser));
|
|
||||||
fieldType.setGeometryQueryBuilder(new VectorGeoShapeQueryProcessor());
|
fieldType.setGeometryQueryBuilder(new VectorGeoShapeQueryProcessor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +125,23 @@ public class GeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geomet
|
||||||
multiFields, copyTo);
|
multiFields, copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addStoredFields(ParseContext context, Geometry geometry) {
|
||||||
|
// noop: we currently do not store geo_shapes
|
||||||
|
// @todo store as geojson string?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
protected void addDocValuesFields(String name, Geometry geometry, List fields, ParseContext context) {
|
||||||
|
// we will throw a mapping exception before we get here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addMultiFields(ParseContext context, Geometry geometry) {
|
||||||
|
// noop (completion suggester currently not compatible with geo_shape)
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doMerge(Mapper mergeWith) {
|
protected void doMerge(Mapper mergeWith) {
|
||||||
if (mergeWith instanceof LegacyGeoShapeFieldMapper) {
|
if (mergeWith instanceof LegacyGeoShapeFieldMapper) {
|
||||||
|
|
|
@ -49,7 +49,7 @@ import static org.elasticsearch.common.geo.GeoUtils.normalizePoint;
|
||||||
/**
|
/**
|
||||||
* Utility class that converts geometries into Lucene-compatible form for indexing in a geo_shape field.
|
* Utility class that converts geometries into Lucene-compatible form for indexing in a geo_shape field.
|
||||||
*/
|
*/
|
||||||
public class GeoShapeIndexer implements AbstractShapeGeometryFieldMapper.Indexer<Geometry, Geometry> {
|
public class GeoShapeIndexer implements AbstractGeometryFieldMapper.Indexer<Geometry, Geometry> {
|
||||||
|
|
||||||
private final boolean orientation;
|
private final boolean orientation;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.index.mapper;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.lucene.index.IndexOptions;
|
import org.apache.lucene.index.IndexOptions;
|
||||||
|
import org.apache.lucene.index.IndexableField;
|
||||||
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
|
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
|
||||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||||
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
|
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
|
||||||
|
@ -179,7 +180,7 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
|
||||||
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(logger);
|
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(logger);
|
||||||
|
|
||||||
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
||||||
LegacyGeoShapeFieldMapper> {
|
LegacyGeoShapeFieldMapper, LegacyGeoShapeFieldMapper.GeoShapeFieldType> {
|
||||||
|
|
||||||
DeprecatedParameters deprecatedParameters;
|
DeprecatedParameters deprecatedParameters;
|
||||||
|
|
||||||
|
@ -193,8 +194,18 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeoShapeFieldType fieldType() {
|
protected void setGeometryParser(GeoShapeFieldType fieldType) {
|
||||||
return (GeoShapeFieldType)fieldType;
|
fieldType().setGeometryParser(ShapeParser::parse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setGeometryIndexer(LegacyGeoShapeFieldMapper.GeoShapeFieldType fieldType) {
|
||||||
|
fieldType().setGeometryIndexer(new LegacyGeoShapeIndexer(fieldType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setGeometryQueryBuilder(GeoShapeFieldType fieldType) {
|
||||||
|
fieldType().setGeometryQueryBuilder(new LegacyGeoShapeQueryProcessor(fieldType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupFieldTypeDeprecatedParameters(BuilderContext context) {
|
private void setupFieldTypeDeprecatedParameters(BuilderContext context) {
|
||||||
|
@ -266,16 +277,6 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
|
||||||
protected void setupFieldType(BuilderContext context) {
|
protected void setupFieldType(BuilderContext context) {
|
||||||
super.setupFieldType(context);
|
super.setupFieldType(context);
|
||||||
|
|
||||||
fieldType().setGeometryIndexer(new LegacyGeoShapeIndexer(fieldType()));
|
|
||||||
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()) {
|
|
||||||
throw new IllegalArgumentException("name cannot be empty string");
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup the deprecated parameters and the prefix tree configuration
|
// setup the deprecated parameters and the prefix tree configuration
|
||||||
setupFieldTypeDeprecatedParameters(context);
|
setupFieldTypeDeprecatedParameters(context);
|
||||||
setupPrefixTrees();
|
setupPrefixTrees();
|
||||||
|
@ -299,7 +300,7 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class GeoShapeFieldType extends AbstractShapeGeometryFieldType {
|
public static final class GeoShapeFieldType extends AbstractShapeGeometryFieldType<ShapeBuilder<?, ?, ?>, Shape> {
|
||||||
|
|
||||||
private String tree = DeprecatedParameters.Defaults.TREE;
|
private String tree = DeprecatedParameters.Defaults.TREE;
|
||||||
private SpatialStrategy strategy = DeprecatedParameters.Defaults.STRATEGY;
|
private SpatialStrategy strategy = DeprecatedParameters.Defaults.STRATEGY;
|
||||||
|
@ -483,6 +484,21 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
|
||||||
return (GeoShapeFieldType) super.fieldType();
|
return (GeoShapeFieldType) super.fieldType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addStoredFields(ParseContext context, Shape geometry) {
|
||||||
|
// noop: we do not store geo_shapes; and will not store legacy geo_shape types
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addDocValuesFields(String name, Shape geometry, List<IndexableField> fields, ParseContext context) {
|
||||||
|
// doc values are not supported
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addMultiFields(ParseContext context, Shape geometry) {
|
||||||
|
// noop (completion suggester currently not compatible with geo_shape)
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
public void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
||||||
super.doXContentBody(builder, includeDefaults, params);
|
super.doXContentBody(builder, includeDefaults, params);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class LegacyGeoShapeIndexer implements AbstractShapeGeometryFieldMapper.Indexer<ShapeBuilder<?, ?, ?>, Shape> {
|
public class LegacyGeoShapeIndexer implements AbstractGeometryFieldMapper.Indexer<ShapeBuilder<?, ?, ?>, Shape> {
|
||||||
|
|
||||||
private LegacyGeoShapeFieldMapper.GeoShapeFieldType fieldType;
|
private LegacyGeoShapeFieldMapper.GeoShapeFieldType fieldType;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.elasticsearch.index.query.QueryShardContext;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -180,8 +181,9 @@ public class ExternalMapper extends FieldMapper {
|
||||||
// Let's add a Dummy Point
|
// Let's add a Dummy Point
|
||||||
Double lat = 42.0;
|
Double lat = 42.0;
|
||||||
Double lng = 51.0;
|
Double lng = 51.0;
|
||||||
GeoPoint point = new GeoPoint(lat, lng);
|
ArrayList<GeoPoint> points = new ArrayList<>();
|
||||||
pointMapper.parse(context.createExternalValueContext(point));
|
points.add(new GeoPoint(lat, lng));
|
||||||
|
pointMapper.parse(context.createExternalValueContext(points));
|
||||||
|
|
||||||
// Let's add a Dummy Shape
|
// Let's add a Dummy Shape
|
||||||
if (shapeMapper instanceof GeoShapeFieldMapper) {
|
if (shapeMapper instanceof GeoShapeFieldMapper) {
|
||||||
|
|
|
@ -41,9 +41,9 @@ import java.util.Collection;
|
||||||
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
|
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
|
||||||
import static org.elasticsearch.geometry.utils.Geohash.stringEncode;
|
import static org.elasticsearch.geometry.utils.Geohash.stringEncode;
|
||||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_MALFORMED;
|
import static org.elasticsearch.index.mapper.AbstractGeometryFieldMapper.Names.IGNORE_MALFORMED;
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.NULL_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper.Names.NULL_VALUE;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
@ -407,7 +407,7 @@ public class GeoPointFieldMapperTests extends ESSingleNodeTestCase {
|
||||||
XContentType.JSON));
|
XContentType.JSON));
|
||||||
|
|
||||||
assertThat(doc.rootDoc().getField("location"), notNullValue());
|
assertThat(doc.rootDoc().getField("location"), notNullValue());
|
||||||
BytesRef defaultValue = doc.rootDoc().getField("location").binaryValue();
|
BytesRef defaultValue = doc.rootDoc().getBinaryValue("location");
|
||||||
|
|
||||||
doc = defaultMapper.parse(new SourceToParse("test", "type", "1",
|
doc = defaultMapper.parse(new SourceToParse("test", "type", "1",
|
||||||
BytesReference.bytes(XContentFactory.jsonBuilder()
|
BytesReference.bytes(XContentFactory.jsonBuilder()
|
||||||
|
@ -416,7 +416,7 @@ public class GeoPointFieldMapperTests extends ESSingleNodeTestCase {
|
||||||
.endObject()),
|
.endObject()),
|
||||||
XContentType.JSON));
|
XContentType.JSON));
|
||||||
// Shouldn't matter if we specify the value explicitly or use null value
|
// Shouldn't matter if we specify the value explicitly or use null value
|
||||||
assertThat(defaultValue, equalTo(doc.rootDoc().getField("location").binaryValue()));
|
assertThat(defaultValue, equalTo(doc.rootDoc().getBinaryValue("location")));
|
||||||
|
|
||||||
doc = defaultMapper.parse(new SourceToParse("test", "type", "1",
|
doc = defaultMapper.parse(new SourceToParse("test", "type", "1",
|
||||||
BytesReference.bytes(XContentFactory.jsonBuilder()
|
BytesReference.bytes(XContentFactory.jsonBuilder()
|
||||||
|
@ -425,7 +425,7 @@ public class GeoPointFieldMapperTests extends ESSingleNodeTestCase {
|
||||||
.endObject()),
|
.endObject()),
|
||||||
XContentType.JSON));
|
XContentType.JSON));
|
||||||
// Shouldn't matter if we specify the value explicitly or use null value
|
// Shouldn't matter if we specify the value explicitly or use null value
|
||||||
assertThat(defaultValue, not(equalTo(doc.rootDoc().getField("location").binaryValue())));
|
assertThat(defaultValue, not(equalTo(doc.rootDoc().getBinaryValue("location"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,7 +34,7 @@ import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
|
|
@ -45,7 +45,7 @@ import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
|
|
@ -25,19 +25,19 @@ import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a point in the cartesian space.
|
* Represents a point in the cartesian space.
|
||||||
*/
|
*/
|
||||||
public final class CartesianPoint implements ToXContentFragment {
|
public class CartesianPoint implements ToXContentFragment {
|
||||||
|
|
||||||
private static final ParseField X_FIELD = new ParseField("x");
|
private static final ParseField X_FIELD = new ParseField("x");
|
||||||
private static final ParseField Y_FIELD = new ParseField("y");
|
private static final ParseField Y_FIELD = new ParseField("y");
|
||||||
private static final ParseField Z_FIELD = new ParseField("z");
|
private static final ParseField Z_FIELD = new ParseField("z");
|
||||||
|
|
||||||
private float x;
|
protected float x;
|
||||||
private float y;
|
protected float y;
|
||||||
|
|
||||||
public CartesianPoint() {
|
public CartesianPoint() {
|
||||||
}
|
}
|
||||||
|
@ -267,12 +267,16 @@ public final class CartesianPoint implements ToXContentFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CartesianPoint parsePoint(Object value, boolean ignoreZValue) throws ElasticsearchParseException {
|
public static CartesianPoint parsePoint(Object value, boolean ignoreZValue) throws ElasticsearchParseException {
|
||||||
|
return parsePoint(value, new CartesianPoint(), ignoreZValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CartesianPoint parsePoint(Object value, CartesianPoint point, boolean ignoreZValue) throws ElasticsearchParseException {
|
||||||
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE,
|
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE,
|
||||||
Collections.singletonMap("null_value", value), null)) {
|
Collections.singletonMap("null_value", value), null)) {
|
||||||
parser.nextToken(); // start object
|
parser.nextToken(); // start object
|
||||||
parser.nextToken(); // field name
|
parser.nextToken(); // field name
|
||||||
parser.nextToken(); // field value
|
parser.nextToken(); // field value
|
||||||
return parsePoint(parser, new CartesianPoint(), ignoreZValue);
|
return parsePoint(parser, point, ignoreZValue);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new ElasticsearchParseException("error parsing point", ex);
|
throw new ElasticsearchParseException("error parsing point", ex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,6 @@ package org.elasticsearch.xpack.spatial.index.mapper;
|
||||||
import org.apache.lucene.document.LatLonShape;
|
import org.apache.lucene.document.LatLonShape;
|
||||||
import org.apache.lucene.document.ShapeField;
|
import org.apache.lucene.document.ShapeField;
|
||||||
import org.apache.lucene.index.IndexableField;
|
import org.apache.lucene.index.IndexableField;
|
||||||
import org.apache.lucene.index.Term;
|
|
||||||
import org.apache.lucene.search.DocValuesFieldExistsQuery;
|
|
||||||
import org.apache.lucene.search.Query;
|
|
||||||
import org.apache.lucene.search.TermQuery;
|
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.common.Explicit;
|
import org.elasticsearch.common.Explicit;
|
||||||
|
@ -23,7 +19,6 @@ import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||||
import org.elasticsearch.geometry.Geometry;
|
import org.elasticsearch.geometry.Geometry;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
|
|
||||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.GeoShapeIndexer;
|
import org.elasticsearch.index.mapper.GeoShapeIndexer;
|
||||||
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
|
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
|
||||||
|
@ -31,7 +26,6 @@ import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
import org.elasticsearch.index.mapper.MapperParsingException;
|
import org.elasticsearch.index.mapper.MapperParsingException;
|
||||||
import org.elasticsearch.index.mapper.ParseContext;
|
import org.elasticsearch.index.mapper.ParseContext;
|
||||||
import org.elasticsearch.index.mapper.TypeParsers;
|
import org.elasticsearch.index.mapper.TypeParsers;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
|
||||||
import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
|
import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
|
||||||
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
|
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
|
||||||
import org.elasticsearch.xpack.spatial.index.fielddata.AbstractLatLonShapeDVIndexFieldData;
|
import org.elasticsearch.xpack.spatial.index.fielddata.AbstractLatLonShapeDVIndexFieldData;
|
||||||
|
@ -67,13 +61,11 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class GeoShapeWithDocValuesFieldMapper extends GeoShapeFieldMapper {
|
public class GeoShapeWithDocValuesFieldMapper extends GeoShapeFieldMapper {
|
||||||
public static final String CONTENT_TYPE = "geo_shape";
|
public static final String CONTENT_TYPE = "geo_shape";
|
||||||
|
|
||||||
|
|
||||||
private Explicit<Boolean> docValues;
|
private Explicit<Boolean> docValues;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
||||||
GeoShapeWithDocValuesFieldMapper> {
|
GeoShapeWithDocValuesFieldMapper, GeoShapeWithDocValuesFieldType> {
|
||||||
public Builder(String name) {
|
public Builder(String name) {
|
||||||
super (name, new GeoShapeWithDocValuesFieldType(), new GeoShapeWithDocValuesFieldType());
|
super (name, new GeoShapeWithDocValuesFieldType(), new GeoShapeWithDocValuesFieldType());
|
||||||
}
|
}
|
||||||
|
@ -100,25 +92,34 @@ public class GeoShapeWithDocValuesFieldMapper extends GeoShapeFieldMapper {
|
||||||
return new Explicit<>(fieldType.hasDocValues(), false);
|
return new Explicit<>(fieldType.hasDocValues(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupFieldType(BuilderContext context) {
|
|
||||||
super.setupFieldType(context);
|
|
||||||
|
|
||||||
GeoShapeWithDocValuesFieldType fieldType = (GeoShapeWithDocValuesFieldType)fieldType();
|
|
||||||
boolean orientation = fieldType.orientation() == ShapeBuilder.Orientation.RIGHT;
|
|
||||||
|
|
||||||
GeometryParser geometryParser = new GeometryParser(orientation, coerce(context).value(), ignoreZValue().value());
|
|
||||||
|
|
||||||
fieldType.setGeometryIndexer(new GeoShapeIndexer(orientation, fieldType.name()) {
|
|
||||||
@Override
|
@Override
|
||||||
public List<IndexableField> indexShape(ParseContext context, Geometry shape) {
|
protected void setGeometryParser(GeoShapeWithDocValuesFieldType ft) {
|
||||||
List<IndexableField> fields = super.indexShape(context, shape);
|
// @todo check coerce
|
||||||
if (fieldType().hasDocValues()) {
|
GeometryParser geometryParser = new GeometryParser(ft.orientation().getAsBoolean(), coerce().value(),
|
||||||
|
ignoreZValue().value());
|
||||||
|
ft.setGeometryParser( (parser, mapper) -> geometryParser.parse(parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setGeometryIndexer(GeoShapeWithDocValuesFieldType fieldType) {
|
||||||
|
fieldType.setGeometryIndexer(new GeoShapeIndexer(fieldType.orientation().getAsBoolean(), fieldType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setGeometryQueryBuilder(GeoShapeWithDocValuesFieldType fieldType) {
|
||||||
|
fieldType.setGeometryQueryBuilder(new VectorGeoShapeQueryProcessor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
protected void addDocValuesFields(String name, Geometry shape, List fields, ParseContext context) {
|
||||||
CentroidCalculator calculator = new CentroidCalculator(shape);
|
CentroidCalculator calculator = new CentroidCalculator(shape);
|
||||||
final byte[] scratch = new byte[7 * Integer.BYTES];
|
final byte[] scratch = new byte[7 * Integer.BYTES];
|
||||||
// doc values are generated from the indexed fields.
|
// doc values are generated from the indexed fields.
|
||||||
ShapeField.DecodedTriangle[] triangles = new ShapeField.DecodedTriangle[fields.size()];
|
ShapeField.DecodedTriangle[] triangles = new ShapeField.DecodedTriangle[fields.size()];
|
||||||
for (int i = 0; i < fields.size(); i++) {
|
for (int i = 0; i < fields.size(); i++) {
|
||||||
BytesRef bytesRef = fields.get(i).binaryValue();
|
BytesRef bytesRef = ((List<IndexableField>)fields).get(i).binaryValue();
|
||||||
assert bytesRef.length == 7 * Integer.BYTES;
|
assert bytesRef.length == 7 * Integer.BYTES;
|
||||||
System.arraycopy(bytesRef.bytes, bytesRef.offset, scratch, 0, 7 * Integer.BYTES);
|
System.arraycopy(bytesRef.bytes, bytesRef.offset, scratch, 0, 7 * Integer.BYTES);
|
||||||
ShapeField.decodeTriangle(scratch, triangles[i] = new ShapeField.DecodedTriangle());
|
ShapeField.decodeTriangle(scratch, triangles[i] = new ShapeField.DecodedTriangle());
|
||||||
|
@ -128,17 +129,11 @@ public class GeoShapeWithDocValuesFieldMapper extends GeoShapeFieldMapper {
|
||||||
if (docValuesField == null) {
|
if (docValuesField == null) {
|
||||||
docValuesField = new BinaryGeoShapeDocValuesField(name, triangles, calculator);
|
docValuesField = new BinaryGeoShapeDocValuesField(name, triangles, calculator);
|
||||||
context.doc().addWithKey(name, docValuesField);
|
context.doc().addWithKey(name, docValuesField);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
docValuesField.add(triangles, calculator);
|
docValuesField.add(triangles, calculator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fieldType.setGeometryParser( (parser, mapper) -> geometryParser.parse(parser));
|
|
||||||
fieldType.setGeometryQueryBuilder(new VectorGeoShapeQueryProcessor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class GeoShapeWithDocValuesFieldType extends GeoShapeFieldMapper.GeoShapeFieldType {
|
public static final class GeoShapeWithDocValuesFieldType extends GeoShapeFieldMapper.GeoShapeFieldType {
|
||||||
public GeoShapeWithDocValuesFieldType() {
|
public GeoShapeWithDocValuesFieldType() {
|
||||||
|
@ -154,15 +149,6 @@ public class GeoShapeWithDocValuesFieldMapper extends GeoShapeFieldMapper {
|
||||||
return new AbstractLatLonShapeDVIndexFieldData.Builder();
|
return new AbstractLatLonShapeDVIndexFieldData.Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Query existsQuery(QueryShardContext context) {
|
|
||||||
if (hasDocValues()) {
|
|
||||||
return new DocValuesFieldExistsQuery(name());
|
|
||||||
} else {
|
|
||||||
return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValuesSourceType getValuesSourceType() {
|
public ValuesSourceType getValuesSourceType() {
|
||||||
return GeoShapeValuesSourceType.instance();
|
return GeoShapeValuesSourceType.instance();
|
||||||
|
|
|
@ -8,106 +8,81 @@ package org.elasticsearch.xpack.spatial.index.mapper;
|
||||||
import org.apache.lucene.document.StoredField;
|
import org.apache.lucene.document.StoredField;
|
||||||
import org.apache.lucene.document.XYDocValuesField;
|
import org.apache.lucene.document.XYDocValuesField;
|
||||||
import org.apache.lucene.document.XYPointField;
|
import org.apache.lucene.document.XYPointField;
|
||||||
import org.apache.lucene.index.IndexOptions;
|
import org.apache.lucene.index.IndexableField;
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
|
||||||
import org.elasticsearch.common.Explicit;
|
import org.elasticsearch.common.Explicit;
|
||||||
import org.elasticsearch.common.ParseField;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
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.XContentParser;
|
||||||
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
|
import org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.ArrayValueMapperParser;
|
import org.elasticsearch.index.mapper.ArrayValueMapperParser;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
import org.elasticsearch.index.mapper.Mapper;
|
|
||||||
import org.elasticsearch.index.mapper.MapperParsingException;
|
|
||||||
import org.elasticsearch.index.mapper.ParseContext;
|
import org.elasticsearch.index.mapper.ParseContext;
|
||||||
import org.elasticsearch.xpack.spatial.common.CartesianPoint;
|
import org.elasticsearch.xpack.spatial.common.CartesianPoint;
|
||||||
import org.elasticsearch.xpack.spatial.index.query.ShapeQueryPointProcessor;
|
import org.elasticsearch.xpack.spatial.index.query.ShapeQueryPointProcessor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.TypeParsers.parseField;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field Mapper for point type.
|
* Field Mapper for point type.
|
||||||
*
|
*
|
||||||
* Uses lucene 8 XYPoint encoding
|
* Uses lucene 8 XYPoint encoding
|
||||||
*/
|
*/
|
||||||
public class PointFieldMapper extends AbstractGeometryFieldMapper implements ArrayValueMapperParser {
|
public class PointFieldMapper extends AbstractPointGeometryFieldMapper<List<? extends CartesianPoint>, List<? extends CartesianPoint>>
|
||||||
|
implements ArrayValueMapperParser {
|
||||||
public static final String CONTENT_TYPE = "point";
|
public static final String CONTENT_TYPE = "point";
|
||||||
|
|
||||||
public static class Names extends AbstractGeometryFieldMapper.Names {
|
public static class Builder extends AbstractPointGeometryFieldMapper.Builder<Builder, PointFieldMapper, PointFieldType> {
|
||||||
public static final ParseField NULL_VALUE = new ParseField("null_value");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder extends AbstractGeometryFieldMapper.Builder<Builder, PointFieldMapper> {
|
|
||||||
public Builder(String name) {
|
public Builder(String name) {
|
||||||
super(name, new PointFieldType(), new PointFieldType());
|
super(name, new PointFieldType(), new PointFieldType());
|
||||||
builder = this;
|
builder = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public PointFieldMapper build(BuilderContext context, String simpleName, MappedFieldType fieldType,
|
public PointFieldMapper build(BuilderContext context, String simpleName, MappedFieldType fieldType,
|
||||||
MappedFieldType defaultFieldType, Settings indexSettings,
|
MappedFieldType defaultFieldType, Settings indexSettings,
|
||||||
MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
|
MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
|
||||||
CopyTo copyTo) {
|
Explicit<Boolean> ignoreZValue, CopyTo copyTo) {
|
||||||
setupFieldType(context);
|
setupFieldType(context);
|
||||||
return new PointFieldMapper(simpleName, fieldType, defaultFieldType, indexSettings, multiFields,
|
return new PointFieldMapper(simpleName, fieldType, defaultFieldType, indexSettings, multiFields,
|
||||||
ignoreMalformed, ignoreZValue(context), copyTo);
|
ignoreMalformed, ignoreZValue(context), copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PointFieldType fieldType() {
|
protected void setGeometryParser() {
|
||||||
return (PointFieldType)fieldType;
|
PointParser<ParsedCartesianPoint> pointParser = new PointParser<>();
|
||||||
|
fieldType().setGeometryParser((parser, mapper) -> pointParser.parse(parser, mapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PointFieldMapper build(BuilderContext context) {
|
@SuppressWarnings("unchecked")
|
||||||
return build(context, name, fieldType, defaultFieldType, context.indexSettings(),
|
protected void setGeometryIndexer(PointFieldType fieldType) {
|
||||||
multiFieldsBuilder.build(this, context), ignoreMalformed(context), copyTo);
|
fieldType.setGeometryIndexer(new PointIndexer(fieldType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupFieldType(BuilderContext context) {
|
protected void setGeometryQueryBuilder(PointFieldType fieldType) {
|
||||||
super.setupFieldType(context);
|
fieldType.setGeometryQueryBuilder(new ShapeQueryPointProcessor());
|
||||||
|
|
||||||
fieldType().setGeometryQueryBuilder(new ShapeQueryPointProcessor());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TypeParser extends AbstractGeometryFieldMapper.TypeParser<Builder> {
|
@Override
|
||||||
|
protected ParsedPoint newParsedPoint() {
|
||||||
|
return new ParsedCartesianPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TypeParser extends AbstractPointGeometryFieldMapper.TypeParser<CartesianPoint, Builder> {
|
||||||
@Override
|
@Override
|
||||||
protected Builder newBuilder(String name, Map<String, Object> params) {
|
protected Builder newBuilder(String name, Map<String, Object> params) {
|
||||||
return new PointFieldMapper.Builder(name);
|
return new PointFieldMapper.Builder(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
protected CartesianPoint parseNullValue(Object nullValue, boolean ignoreZValue, boolean ignoreMalformed) {
|
||||||
public Builder parse(String name, Map<String, Object> node, Map<String, Object> params, ParserContext parserContext) {
|
ParsedCartesianPoint point = new ParsedCartesianPoint();
|
||||||
Builder builder = super.parse(name, node, params, parserContext);
|
CartesianPoint.parsePoint(nullValue, point, ignoreZValue);
|
||||||
parseField(builder, name, node, parserContext);
|
|
||||||
Object nullValue = null;
|
|
||||||
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
|
|
||||||
Map.Entry<String, Object> entry = iterator.next();
|
|
||||||
String propName = entry.getKey();
|
|
||||||
Object propNode = entry.getValue();
|
|
||||||
|
|
||||||
if (Names.NULL_VALUE.match(propName, LoggingDeprecationHandler.INSTANCE)) {
|
|
||||||
if (propNode == null) {
|
|
||||||
throw new MapperParsingException("Property [null_value] cannot be null.");
|
|
||||||
}
|
|
||||||
nullValue = propNode;
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nullValue != null) {
|
|
||||||
boolean ignoreMalformed = builder.ignoreMalformed().value();
|
|
||||||
boolean ignoreZValue = builder.ignoreZValue().value();
|
|
||||||
CartesianPoint point = CartesianPoint.parsePoint(nullValue, ignoreZValue);
|
|
||||||
if (ignoreMalformed == false) {
|
if (ignoreMalformed == false) {
|
||||||
if (Float.isFinite(point.getX()) == false) {
|
if (Float.isFinite(point.getX()) == false) {
|
||||||
throw new IllegalArgumentException("illegal x value [" + point.getX() + "]");
|
throw new IllegalArgumentException("illegal x value [" + point.getX() + "]");
|
||||||
|
@ -116,21 +91,45 @@ public class PointFieldMapper extends AbstractGeometryFieldMapper implements Arr
|
||||||
throw new IllegalArgumentException("illegal y value [" + point.getY() + "]");
|
throw new IllegalArgumentException("illegal y value [" + point.getY() + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.nullValue(point);
|
return point;
|
||||||
}
|
}
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses geopoint represented as an object or an array, ignores malformed geopoints if needed
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void parsePointIgnoringMalformed(XContentParser parser, ParsedPoint point) throws IOException {
|
||||||
|
super.parsePointIgnoringMalformed(parser, point);
|
||||||
|
CartesianPoint.parsePoint(parser, (CartesianPoint)point, ignoreZValue().value());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PointFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
|
public PointFieldMapper(String simpleName, MappedFieldType fieldType, MappedFieldType defaultFieldType,
|
||||||
Settings indexSettings, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
|
Settings indexSettings, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
|
||||||
Explicit<Boolean> ignoreZValue, CopyTo copyTo) {
|
Explicit<Boolean> ignoreZValue, CopyTo copyTo) {
|
||||||
super(simpleName, fieldType, defaultFieldType, indexSettings, ignoreMalformed, ignoreZValue, multiFields, copyTo);
|
super(simpleName, fieldType, defaultFieldType, indexSettings, multiFields, ignoreMalformed, ignoreZValue, copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doMerge(Mapper mergeWith) {
|
@SuppressWarnings("unchecked")
|
||||||
super.doMerge(mergeWith);
|
protected void addStoredFields(ParseContext context, List<? extends CartesianPoint> points) {
|
||||||
|
for (CartesianPoint point : points) {
|
||||||
|
context.doc().add(new StoredField(fieldType().name(), point.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void addDocValuesFields(String name, List<? extends CartesianPoint> points, List<IndexableField> fields,
|
||||||
|
ParseContext context) {
|
||||||
|
for (CartesianPoint point : points) {
|
||||||
|
context.doc().add(new XYDocValuesField(fieldType().name(), point.getX(), point.getY()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addMultiFields(ParseContext context, List<? extends CartesianPoint> points) {
|
||||||
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -138,11 +137,14 @@ public class PointFieldMapper extends AbstractGeometryFieldMapper implements Arr
|
||||||
return CONTENT_TYPE;
|
return CONTENT_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PointFieldType extends AbstractGeometryFieldType {
|
@Override
|
||||||
|
public PointFieldType fieldType() {
|
||||||
|
return (PointFieldType)fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PointFieldType extends AbstractPointGeometryFieldType<List<ParsedCartesianPoint>, List<ParsedCartesianPoint>> {
|
||||||
public PointFieldType() {
|
public PointFieldType() {
|
||||||
super();
|
super();
|
||||||
setHasDocValues(true);
|
|
||||||
setDimensions(2, Integer.BYTES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PointFieldType(PointFieldType ref) {
|
PointFieldType(PointFieldType ref) {
|
||||||
|
@ -160,90 +162,87 @@ public class PointFieldMapper extends AbstractGeometryFieldMapper implements Arr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void parse(ParseContext context, CartesianPoint point) throws IOException {
|
protected static class ParsedCartesianPoint extends CartesianPoint implements ParsedPoint {
|
||||||
|
@Override
|
||||||
if (fieldType().indexOptions() != IndexOptions.NONE) {
|
public void validate(String fieldName) {
|
||||||
context.doc().add(new XYPointField(fieldType().name(), point.getX(), point.getY()));
|
if (Float.isFinite(getX()) == false) {
|
||||||
|
throw new IllegalArgumentException("illegal x value [" + getX() + "] for " + fieldName);
|
||||||
}
|
}
|
||||||
if (fieldType().stored()) {
|
if (Float.isFinite(getY()) == false) {
|
||||||
context.doc().add(new StoredField(fieldType().name(), point.toString()));
|
throw new IllegalArgumentException("illegal y value [" + getY() + "] for " + fieldName);
|
||||||
}
|
|
||||||
if (fieldType.hasDocValues()) {
|
|
||||||
context.doc().add(new XYDocValuesField(fieldType().name(), point.getX(), point.getY()));
|
|
||||||
} else if (fieldType().stored() || fieldType().indexOptions() != IndexOptions.NONE) {
|
|
||||||
createFieldNamesField(context);
|
|
||||||
}
|
|
||||||
// if the mapping contains multi-fields then throw an error?
|
|
||||||
if (multiFields.iterator().hasNext()) {
|
|
||||||
throw new ElasticsearchParseException("[{}] field type does not accept multi-fields", CONTENT_TYPE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parse(ParseContext context) throws IOException {
|
public void normalize(String fieldName) {
|
||||||
context.path().add(simpleName());
|
// noop
|
||||||
|
|
||||||
try {
|
|
||||||
CartesianPoint sparse = context.parseExternalValue(CartesianPoint.class);
|
|
||||||
|
|
||||||
if (sparse != null) {
|
|
||||||
parse(context, sparse);
|
|
||||||
} else {
|
|
||||||
sparse = new CartesianPoint();
|
|
||||||
XContentParser.Token token = context.parser().currentToken();
|
|
||||||
if (token == XContentParser.Token.START_ARRAY) {
|
|
||||||
token = context.parser().nextToken();
|
|
||||||
if (token == XContentParser.Token.VALUE_NUMBER) {
|
|
||||||
float x = context.parser().floatValue();
|
|
||||||
context.parser().nextToken();
|
|
||||||
float y = context.parser().floatValue();
|
|
||||||
token = context.parser().nextToken();
|
|
||||||
if (token == XContentParser.Token.VALUE_NUMBER) {
|
|
||||||
CartesianPoint.assertZValue(ignoreZValue.value(), context.parser().floatValue());
|
|
||||||
} else if (token != XContentParser.Token.END_ARRAY) {
|
|
||||||
throw new ElasticsearchParseException("[{}] field type does not accept > 3 dimensions", CONTENT_TYPE);
|
|
||||||
}
|
|
||||||
parse(context, sparse.reset(x, y));
|
|
||||||
} else {
|
|
||||||
while (token != XContentParser.Token.END_ARRAY) {
|
|
||||||
parsePointIgnoringMalformed(context, sparse);
|
|
||||||
token = context.parser().nextToken();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (token == XContentParser.Token.VALUE_NULL) {
|
|
||||||
if (fieldType.nullValue() != null) {
|
|
||||||
parse(context, (CartesianPoint) fieldType.nullValue());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parsePointIgnoringMalformed(context, sparse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new MapperParsingException("failed to parse field [{}] of type [{}]", ex, fieldType().name(), fieldType().typeName());
|
|
||||||
}
|
|
||||||
|
|
||||||
context.path().remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses point represented as an object or an array, ignores malformed points if needed
|
|
||||||
*/
|
|
||||||
private void parsePointIgnoringMalformed(ParseContext context, CartesianPoint sparse) throws IOException {
|
|
||||||
try {
|
|
||||||
parse(context, CartesianPoint.parsePoint(context.parser(), sparse, ignoreZValue().value()));
|
|
||||||
} catch (ElasticsearchParseException e) {
|
|
||||||
if (ignoreMalformed.value() == false) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
context.addIgnoredField(fieldType.name());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
public boolean isNormalizable(double coord) {
|
||||||
super.doXContentBody(builder, includeDefaults, params);
|
return false;
|
||||||
if (includeDefaults || fieldType().nullValue() != null) {
|
}
|
||||||
builder.field(Names.NULL_VALUE.getPreferredName(), fieldType().nullValue());
|
|
||||||
|
@Override
|
||||||
|
public void resetCoords(double x, double y) {
|
||||||
|
this.reset((float)x, (float)y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
double oX;
|
||||||
|
double oY;
|
||||||
|
if (other instanceof CartesianPoint) {
|
||||||
|
CartesianPoint o = (CartesianPoint)other;
|
||||||
|
oX = o.getX();
|
||||||
|
oY = o.getY();
|
||||||
|
} else if (other instanceof ParsedCartesianPoint == false) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
ParsedCartesianPoint o = (ParsedCartesianPoint)other;
|
||||||
|
oX = o.getX();
|
||||||
|
oY = o.getY();
|
||||||
|
}
|
||||||
|
if (Double.compare(oX, x) != 0) return false;
|
||||||
|
if (Double.compare(oY, y) != 0) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class PointIndexer implements Indexer<List<ParsedCartesianPoint>, List<ParsedCartesianPoint>> {
|
||||||
|
protected final PointFieldType fieldType;
|
||||||
|
|
||||||
|
PointIndexer(PointFieldType fieldType) {
|
||||||
|
this.fieldType = fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ParsedCartesianPoint> prepareForIndexing(List<ParsedCartesianPoint> points) {
|
||||||
|
if (points == null || points.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Class<List<ParsedCartesianPoint>> processedClass() {
|
||||||
|
return (Class<List<ParsedCartesianPoint>>)(Object)List.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IndexableField> indexShape(ParseContext context, List<ParsedCartesianPoint> points) {
|
||||||
|
ArrayList<IndexableField> fields = new ArrayList<>(1);
|
||||||
|
for (ParsedCartesianPoint point : points) {
|
||||||
|
fields.add(new XYPointField(fieldType.name(), point.getX(), point.getY()));
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,10 @@ import org.elasticsearch.geometry.Geometry;
|
||||||
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
import org.elasticsearch.index.mapper.MapperParsingException;
|
import org.elasticsearch.index.mapper.MapperParsingException;
|
||||||
|
import org.elasticsearch.index.mapper.ParseContext;
|
||||||
import org.elasticsearch.xpack.spatial.index.query.ShapeQueryProcessor;
|
import org.elasticsearch.xpack.spatial.index.query.ShapeQueryProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +45,7 @@ public class ShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geometry,
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
public static class Builder extends AbstractShapeGeometryFieldMapper.Builder<AbstractShapeGeometryFieldMapper.Builder,
|
||||||
ShapeFieldMapper> {
|
ShapeFieldMapper, ShapeFieldType> {
|
||||||
|
|
||||||
public Builder(String name) {
|
public Builder(String name) {
|
||||||
super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
|
super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
|
||||||
|
@ -58,21 +60,20 @@ public class ShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geometry,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ShapeFieldType fieldType() {
|
protected void setGeometryParser(ShapeFieldType fieldType) {
|
||||||
return (ShapeFieldType)fieldType;
|
GeometryParser geometryParser = new GeometryParser(fieldType.orientation().getAsBoolean(),
|
||||||
|
coerce().value(), ignoreZValue().value());
|
||||||
|
fieldType().setGeometryParser((parser, mapper) -> geometryParser.parse(parser));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupFieldType(BuilderContext context) {
|
protected void setGeometryIndexer(ShapeFieldType fieldType) {
|
||||||
super.setupFieldType(context);
|
fieldType.setGeometryIndexer(new ShapeIndexer(fieldType.name()));
|
||||||
|
}
|
||||||
|
|
||||||
GeometryParser geometryParser = new GeometryParser(orientation == Orientation.RIGHT,
|
@Override
|
||||||
coerce(context).value(), ignoreZValue().value());
|
protected void setGeometryQueryBuilder(ShapeFieldType fieldType) {
|
||||||
|
fieldType.setGeometryQueryBuilder(new ShapeQueryProcessor());
|
||||||
fieldType().setGeometryIndexer(new ShapeIndexer(fieldType().name()));
|
|
||||||
fieldType().setGeometryParser((parser, mapper) -> geometryParser.parse(parser));
|
|
||||||
fieldType().setGeometryQueryBuilder(new ShapeQueryProcessor());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +125,23 @@ public class ShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geometry,
|
||||||
multiFields, copyTo);
|
multiFields, copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addStoredFields(ParseContext context, Geometry geometry) {
|
||||||
|
// noop: we currently do not store geo_shapes
|
||||||
|
// @todo store as geojson string?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
protected void addDocValuesFields(String name, Geometry geometry, List fields, ParseContext context) {
|
||||||
|
// we should throw a mapping exception before we get here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addMultiFields(ParseContext context, Geometry geometry) {
|
||||||
|
// noop (completion suggester currently not compatible with geo_shape)
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String contentType() {
|
protected String contentType() {
|
||||||
return CONTENT_TYPE;
|
return CONTENT_TYPE;
|
||||||
|
|
|
@ -81,7 +81,7 @@ public final class CircleProcessor extends AbstractProcessor {
|
||||||
parser.nextToken(); // START_OBJECT
|
parser.nextToken(); // START_OBJECT
|
||||||
parser.nextToken(); // "shape" field key
|
parser.nextToken(); // "shape" field key
|
||||||
parser.nextToken(); // shape value
|
parser.nextToken(); // shape value
|
||||||
GeometryFormat geometryFormat = PARSER.geometryFormat(parser);
|
GeometryFormat<Geometry> geometryFormat = PARSER.geometryFormat(parser);
|
||||||
Geometry geometry = geometryFormat.fromXContent(parser);
|
Geometry geometry = geometryFormat.fromXContent(parser);
|
||||||
if (ShapeType.CIRCLE.equals(geometry.type())) {
|
if (ShapeType.CIRCLE.equals(geometry.type())) {
|
||||||
Circle circle = (Circle) geometry;
|
Circle circle = (Circle) geometry;
|
||||||
|
|
|
@ -47,7 +47,7 @@ import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
|
|
@ -21,8 +21,8 @@ import org.hamcrest.CoreMatchers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
import static org.elasticsearch.xpack.spatial.index.mapper.PointFieldMapper.Names.NULL_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper.Names.NULL_VALUE;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
@ -239,7 +239,7 @@ public class PointFieldMapperTests extends CartesianFieldMapperTests {
|
||||||
XContentType.JSON));
|
XContentType.JSON));
|
||||||
|
|
||||||
assertThat(doc.rootDoc().getField("location"), notNullValue());
|
assertThat(doc.rootDoc().getField("location"), notNullValue());
|
||||||
BytesRef defaultValue = doc.rootDoc().getField("location").binaryValue();
|
BytesRef defaultValue = doc.rootDoc().getBinaryValue("location");
|
||||||
|
|
||||||
doc = defaultMapper.parse(new SourceToParse("test","type", "1",
|
doc = defaultMapper.parse(new SourceToParse("test","type", "1",
|
||||||
BytesReference.bytes(XContentFactory.jsonBuilder()
|
BytesReference.bytes(XContentFactory.jsonBuilder()
|
||||||
|
@ -248,7 +248,7 @@ public class PointFieldMapperTests extends CartesianFieldMapperTests {
|
||||||
.endObject()),
|
.endObject()),
|
||||||
XContentType.JSON));
|
XContentType.JSON));
|
||||||
// Shouldn't matter if we specify the value explicitly or use null value
|
// Shouldn't matter if we specify the value explicitly or use null value
|
||||||
assertThat(defaultValue, equalTo(doc.rootDoc().getField("location").binaryValue()));
|
assertThat(defaultValue, equalTo(doc.rootDoc().getBinaryValue("location")));
|
||||||
|
|
||||||
doc = defaultMapper.parse(new SourceToParse("test","type", "1",
|
doc = defaultMapper.parse(new SourceToParse("test","type", "1",
|
||||||
BytesReference.bytes(XContentFactory.jsonBuilder()
|
BytesReference.bytes(XContentFactory.jsonBuilder()
|
||||||
|
@ -257,7 +257,7 @@ public class PointFieldMapperTests extends CartesianFieldMapperTests {
|
||||||
.endObject()),
|
.endObject()),
|
||||||
XContentType.JSON));
|
XContentType.JSON));
|
||||||
// Shouldn't matter if we specify the value explicitly or use null value
|
// Shouldn't matter if we specify the value explicitly or use null value
|
||||||
assertThat(defaultValue, not(equalTo(doc.rootDoc().getField("location").binaryValue())));
|
assertThat(defaultValue, not(equalTo(doc.rootDoc().getBinaryValue("location"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,7 +21,7 @@ import org.elasticsearch.index.mapper.MapperService;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.elasticsearch.index.mapper.GeoPointFieldMapper.Names.IGNORE_Z_VALUE;
|
import static org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper.Names.IGNORE_Z_VALUE;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue