Refactor PointParser to not take FieldMapper as a parameter (#62950)

Passing FieldMappers to point parsing functions makes trying to build source-only
fields from MappedFieldTypes more complicated. This small refactoring changes
things so that the relevant parsing and factory functions from
AbstractGeometryFieldMapper are instead passed as lambdas to the PointParser
constructor.
This commit is contained in:
Alan Woodward 2020-09-28 13:44:34 +01:00 committed by Alan Woodward
parent 4d43fa8816
commit a3ba24123e
6 changed files with 61 additions and 96 deletions

View File

@ -89,7 +89,7 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
* Parse the given xContent value to an object of type {@link Parsed}. The value can be * Parse the given xContent value to an object of type {@link Parsed}. The value can be
* in any supported format. * in any supported format.
*/ */
public abstract Parsed parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException; public abstract Parsed parse(XContentParser parser) throws IOException, ParseException;
/** /**
* Given a parsed value and a format string, formats the value into a plain Java object. * Given a parsed value and a format string, formats the value into a plain Java object.
@ -106,14 +106,14 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
* it with {@link Parser#format}. However some {@link Parser} implementations override this * it with {@link Parser#format}. However some {@link Parser} implementations override this
* as they can avoid parsing the value if it is already in the right format. * as they can avoid parsing the value if it is already in the right format.
*/ */
public Object parseAndFormatObject(Object value, AbstractGeometryFieldMapper mapper, String format) { public Object parseAndFormatObject(Object value, String format) {
Parsed geometry; Parsed geometry;
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE,
Collections.singletonMap("dummy_field", value), XContentType.JSON)) { Collections.singletonMap("dummy_field", value), XContentType.JSON)) {
parser.nextToken(); // start object parser.nextToken(); // start object
parser.nextToken(); // field name parser.nextToken(); // field name
parser.nextToken(); // field value parser.nextToken(); // field value
geometry = parse(parser, mapper); geometry = parse(parser);
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} catch (ParseException e) { } catch (ParseException e) {
@ -188,7 +188,7 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
AbstractGeometryFieldType<Parsed, Processed> mappedFieldType = fieldType(); AbstractGeometryFieldType<Parsed, Processed> mappedFieldType = fieldType();
Parser<Parsed> geometryParser = mappedFieldType.geometryParser(); Parser<Parsed> geometryParser = mappedFieldType.geometryParser();
Function<Object, Object> valueParser = value -> geometryParser.parseAndFormatObject(value, this, geoFormat); Function<Object, Object> valueParser = value -> geometryParser.parseAndFormatObject(value, geoFormat);
return new SourceValueFetcher(name(), mapperService, parsesArrayValue()) { return new SourceValueFetcher(name(), mapperService, parsesArrayValue()) {
@Override @Override
@ -340,7 +340,7 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
try { try {
Processed shape = context.parseExternalValue(geometryIndexer.processedClass()); Processed shape = context.parseExternalValue(geometryIndexer.processedClass());
if (shape == null) { if (shape == null) {
Parsed geometry = geometryParser.parse(context.parser(), this); Parsed geometry = geometryParser.parse(context.parser());
if (geometry == null) { if (geometry == null) {
return; return;
} }

View File

@ -20,6 +20,7 @@ package org.elasticsearch.index.mapper;
import org.apache.lucene.document.FieldType; import org.apache.lucene.document.FieldType;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.CheckedBiFunction;
import org.elasticsearch.common.Explicit; import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
@ -34,9 +35,11 @@ import org.elasticsearch.geometry.Point;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import static org.elasticsearch.index.mapper.TypeParsers.parseField; import static org.elasticsearch.index.mapper.TypeParsers.parseField;
@ -154,8 +157,6 @@ public abstract class AbstractPointGeometryFieldMapper<Parsed, Processed> extend
return nullValue; return nullValue;
} }
protected abstract ParsedPoint newParsedPoint();
/** represents a Point that has been parsed by {@link PointParser} */ /** represents a Point that has been parsed by {@link PointParser} */
public interface ParsedPoint { public interface ParsedPoint {
void validate(String fieldName); void validate(String fieldName);
@ -167,87 +168,80 @@ public abstract class AbstractPointGeometryFieldMapper<Parsed, Processed> extend
} }
} }
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 */ /** A parser implementation that can parse the various point formats */
public static class PointParser<P extends ParsedPoint> extends Parser<List<P>> { public static class PointParser<P extends ParsedPoint> extends Parser<List<P>> {
/** /**
* Note that this parser is only used for formatting values. * Note that this parser is only used for formatting values.
*/ */
private final GeometryParser geometryParser; private final GeometryParser geometryParser;
private final String field;
private final Supplier<P> pointSupplier;
private final CheckedBiFunction<XContentParser, P, P, IOException> objectParser;
private final P nullValue;
private final boolean ignoreZValue;
private final boolean ignoreMalformed;
public PointParser() { public PointParser(String field,
Supplier<P> pointSupplier,
CheckedBiFunction<XContentParser, P, P, IOException> objectParser,
P nullValue,
boolean ignoreZValue,
boolean ignoreMalformed) {
this.field = field;
this.pointSupplier = pointSupplier;
this.objectParser = objectParser;
this.nullValue = nullValue;
this.ignoreZValue = ignoreZValue;
this.ignoreMalformed = ignoreMalformed;
this.geometryParser = new GeometryParser(true, true, true); this.geometryParser = new GeometryParser(true, true, true);
} }
private P process(P in) {
if (ignoreMalformed == false) {
in.validate(field);
} else {
in.normalize(field);
}
return in;
}
@Override @Override
public List<P> parse(XContentParser parser, AbstractGeometryFieldMapper geometryMapper) throws IOException, ParseException { public List<P> parse(XContentParser parser) throws IOException, ParseException {
AbstractPointGeometryFieldMapper mapper = (AbstractPointGeometryFieldMapper) geometryMapper;
if (parser.currentToken() == XContentParser.Token.START_ARRAY) { if (parser.currentToken() == XContentParser.Token.START_ARRAY) {
XContentParser.Token token = parser.nextToken(); XContentParser.Token token = parser.nextToken();
P point = (P)(mapper.newParsedPoint()); P point = pointSupplier.get();
ArrayList<P> points = new ArrayList(); ArrayList<P> points = new ArrayList<>();
if (token == XContentParser.Token.VALUE_NUMBER) { if (token == XContentParser.Token.VALUE_NUMBER) {
double x = parser.doubleValue(); double x = parser.doubleValue();
parser.nextToken(); parser.nextToken();
double y = parser.doubleValue(); double y = parser.doubleValue();
token = parser.nextToken(); token = parser.nextToken();
if (token == XContentParser.Token.VALUE_NUMBER) { if (token == XContentParser.Token.VALUE_NUMBER) {
GeoPoint.assertZValue((Boolean)(mapper.ignoreZValue().value()), parser.doubleValue()); GeoPoint.assertZValue(ignoreZValue, parser.doubleValue());
} else if (token != XContentParser.Token.END_ARRAY) { } else if (token != XContentParser.Token.END_ARRAY) {
throw new ElasticsearchParseException("[{}] field type does not accept > 3 dimensions", throw new ElasticsearchParseException("field type does not accept > 3 dimensions");
mapper.contentType());
} }
point.resetCoords(x, y); point.resetCoords(x, y);
if ((Boolean)(mapper.ignoreMalformed().value()) == false) { points.add(process(point));
point.validate(mapper.name());
} else {
point.normalize(mapper.name());
}
points.add(point);
} else { } else {
while (token != XContentParser.Token.END_ARRAY) { while (token != XContentParser.Token.END_ARRAY) {
mapper.parsePointIgnoringMalformed(parser, point); points.add(process(objectParser.apply(parser, point)));
points.add(point); point = pointSupplier.get();
point = (P)(mapper.newParsedPoint());
token = parser.nextToken(); token = parser.nextToken();
} }
} }
return points; return points;
} else if (parser.currentToken() == XContentParser.Token.VALUE_NULL) { } else if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
P point = null; if (nullValue == null) {
ArrayList<P> points = null; return Collections.emptyList();
if (mapper.nullValue != null) { }
point = (P)(mapper.nullValue); else {
if ((Boolean)(mapper.ignoreMalformed().value()) == false) { return Collections.singletonList(nullValue);
point.validate(mapper.name());
} else {
point.normalize(mapper.name());
}
points = new ArrayList<>();
points.add(point);
} }
return points;
} else { } else {
P point = (P)mapper.newParsedPoint(); return Collections.singletonList(process(objectParser.apply(parser, pointSupplier.get())));
mapper.parsePointIgnoringMalformed(parser, point);
ArrayList<P> points = new ArrayList();
points.add(point);
return points;
} }
} }

View File

@ -30,7 +30,6 @@ import org.elasticsearch.common.Explicit;
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.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointIndexFieldData; import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointIndexFieldData;
@ -75,7 +74,10 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
MultiFields multiFields, Explicit<Boolean> ignoreMalformed, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) { Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {
GeoPointFieldType ft = new GeoPointFieldType(buildFullName(context), indexed, fieldType.stored(), hasDocValues, meta); GeoPointFieldType ft = new GeoPointFieldType(buildFullName(context), indexed, fieldType.stored(), hasDocValues, meta);
ft.setGeometryParser(new PointParser<>()); ft.setGeometryParser(new PointParser<>(name, ParsedGeoPoint::new, (parser, point) -> {
GeoUtils.parseGeoPoint(parser, point, ignoreZValue().value());
return point;
}, (ParsedGeoPoint) nullValue, ignoreZValue.value(), ignoreMalformed.value()));
ft.setGeometryIndexer(new GeoPointIndexer(ft)); ft.setGeometryIndexer(new GeoPointIndexer(ft));
ft.setGeometryQueryBuilder(new VectorGeoPointShapeQueryProcessor()); ft.setGeometryQueryBuilder(new VectorGeoPointShapeQueryProcessor());
return new GeoPointFieldMapper(name, fieldType, ft, multiFields, ignoreMalformed, ignoreZValue, nullValue, copyTo); return new GeoPointFieldMapper(name, fieldType, ft, multiFields, ignoreMalformed, ignoreZValue, nullValue, copyTo);
@ -105,15 +107,6 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
} }
} }
/**
* 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, FieldType fieldType, MappedFieldType mappedFieldType, public GeoPointFieldMapper(String simpleName, FieldType fieldType, MappedFieldType mappedFieldType,
MultiFields multiFields, Explicit<Boolean> ignoreMalformed, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) { Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {
@ -166,11 +159,6 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
return (GeoPointFieldType)mappedFieldType; return (GeoPointFieldType)mappedFieldType;
} }
@Override
protected ParsedPoint newParsedPoint() {
return new ParsedGeoPoint();
}
public static class GeoPointFieldType extends AbstractPointGeometryFieldType<List<ParsedGeoPoint>, List<? extends GeoPoint>> { public static class GeoPointFieldType extends AbstractPointGeometryFieldType<List<ParsedGeoPoint>, List<? extends GeoPoint>> {
private GeoPointFieldType(String name, boolean indexed, boolean stored, boolean hasDocValues, Map<String, String> meta) { private GeoPointFieldType(String name, boolean indexed, boolean stored, boolean hasDocValues, Map<String, String> meta) {
super(name, indexed, stored, hasDocValues, meta); super(name, indexed, stored, hasDocValues, meta);
@ -251,12 +239,8 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
GeoPoint o = (GeoPoint)other; GeoPoint o = (GeoPoint)other;
oLat = o.lat(); oLat = o.lat();
oLon = o.lon(); oLon = o.lon();
} else if (other instanceof ParsedGeoPoint == false) {
return false;
} else { } else {
ParsedGeoPoint o = (ParsedGeoPoint)other; return false;
oLat = o.lat();
oLon = o.lon();
} }
if (Double.compare(oLat, lat) != 0) return false; if (Double.compare(oLat, lat) != 0) return false;
if (Double.compare(oLon, lon) != 0) return false; if (Double.compare(oLon, lon) != 0) return false;

View File

@ -41,7 +41,7 @@ public class GeoShapeParser extends AbstractGeometryFieldMapper.Parser<Geometry>
} }
@Override @Override
public Geometry parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException { public Geometry parse(XContentParser parser) throws IOException, ParseException {
return geometryParser.parse(parser); return geometryParser.parse(parser);
} }
@ -51,7 +51,7 @@ public class GeoShapeParser extends AbstractGeometryFieldMapper.Parser<Geometry>
} }
@Override @Override
public Object parseAndFormatObject(Object value, AbstractGeometryFieldMapper mapper, String format) { public Object parseAndFormatObject(Object value, String format) {
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE,
Collections.singletonMap("dummy_field", value), XContentType.JSON)) { Collections.singletonMap("dummy_field", value), XContentType.JSON)) {
parser.nextToken(); // start object parser.nextToken(); // start object

View File

@ -298,7 +298,7 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
} }
@Override @Override
public ShapeBuilder<?, ?, ?> parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException { public ShapeBuilder<?, ?, ?> parse(XContentParser parser) throws IOException, ParseException {
return ShapeParser.parse(parser); return ShapeParser.parse(parser);
} }

View File

@ -11,7 +11,6 @@ import org.apache.lucene.document.XYDocValuesField;
import org.apache.lucene.document.XYPointField; import org.apache.lucene.document.XYPointField;
import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.IndexableField;
import org.elasticsearch.common.Explicit; import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
@ -20,7 +19,6 @@ import org.elasticsearch.xpack.spatial.common.CartesianPoint;
import org.elasticsearch.xpack.spatial.index.mapper.PointFieldMapper.ParsedCartesianPoint; import org.elasticsearch.xpack.spatial.index.mapper.PointFieldMapper.ParsedCartesianPoint;
import org.elasticsearch.xpack.spatial.index.query.ShapeQueryPointProcessor; import org.elasticsearch.xpack.spatial.index.query.ShapeQueryPointProcessor;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -45,7 +43,10 @@ public class PointFieldMapper extends AbstractPointGeometryFieldMapper<List<Pars
MultiFields multiFields, Explicit<Boolean> ignoreMalformed, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) { Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {
PointFieldType ft = new PointFieldType(buildFullName(context), indexed, fieldType.stored(), hasDocValues, meta); PointFieldType ft = new PointFieldType(buildFullName(context), indexed, fieldType.stored(), hasDocValues, meta);
ft.setGeometryParser(new PointParser<>()); ft.setGeometryParser(new PointParser<>(name, ParsedCartesianPoint::new, (parser, point) -> {
ParsedCartesianPoint.parsePoint(parser, point, ignoreZValue.value());
return point;
}, (ParsedCartesianPoint) nullValue, ignoreZValue.value(), ignoreMalformed.value()));
ft.setGeometryIndexer(new PointIndexer(ft)); ft.setGeometryIndexer(new PointIndexer(ft));
ft.setGeometryQueryBuilder(new ShapeQueryPointProcessor()); ft.setGeometryQueryBuilder(new ShapeQueryPointProcessor());
return new PointFieldMapper(simpleName, fieldType, ft, multiFields, return new PointFieldMapper(simpleName, fieldType, ft, multiFields,
@ -54,11 +55,6 @@ public class PointFieldMapper extends AbstractPointGeometryFieldMapper<List<Pars
} }
@Override
protected ParsedPoint newParsedPoint() {
return new ParsedCartesianPoint();
}
public static class TypeParser extends AbstractPointGeometryFieldMapper.TypeParser<Builder> { public static class TypeParser extends AbstractPointGeometryFieldMapper.TypeParser<Builder> {
@Override @Override
protected Builder newBuilder(String name, Map<String, Object> params) { protected Builder newBuilder(String name, Map<String, Object> params) {
@ -81,15 +77,6 @@ public class PointFieldMapper extends AbstractPointGeometryFieldMapper<List<Pars
} }
} }
/**
* 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, FieldType fieldType, MappedFieldType mappedFieldType, public PointFieldMapper(String simpleName, FieldType fieldType, MappedFieldType mappedFieldType,
MultiFields multiFields, Explicit<Boolean> ignoreMalformed, MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) { Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {