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
* 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.
@ -106,14 +106,14 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
* 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.
*/
public Object parseAndFormatObject(Object value, AbstractGeometryFieldMapper mapper, String format) {
public Object parseAndFormatObject(Object value, String format) {
Parsed geometry;
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE,
Collections.singletonMap("dummy_field", value), XContentType.JSON)) {
parser.nextToken(); // start object
parser.nextToken(); // field name
parser.nextToken(); // field value
geometry = parse(parser, mapper);
geometry = parse(parser);
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (ParseException e) {
@ -188,7 +188,7 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
AbstractGeometryFieldType<Parsed, Processed> mappedFieldType = fieldType();
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()) {
@Override
@ -340,7 +340,7 @@ public abstract class AbstractGeometryFieldMapper<Parsed, Processed> extends Fie
try {
Processed shape = context.parseExternalValue(geometryIndexer.processedClass());
if (shape == null) {
Parsed geometry = geometryParser.parse(context.parser(), this);
Parsed geometry = geometryParser.parse(context.parser());
if (geometry == null) {
return;
}

View File

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

View File

@ -30,7 +30,6 @@ import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointIndexFieldData;
@ -75,7 +74,10 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {
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.setGeometryQueryBuilder(new VectorGeoPointShapeQueryProcessor());
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,
MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {
@ -166,11 +159,6 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
return (GeoPointFieldType)mappedFieldType;
}
@Override
protected ParsedPoint newParsedPoint() {
return new ParsedGeoPoint();
}
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) {
super(name, indexed, stored, hasDocValues, meta);
@ -251,12 +239,8 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<List<P
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();
return false;
}
if (Double.compare(oLat, lat) != 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
public Geometry parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException {
public Geometry parse(XContentParser parser) throws IOException, ParseException {
return geometryParser.parse(parser);
}
@ -51,7 +51,7 @@ public class GeoShapeParser extends AbstractGeometryFieldMapper.Parser<Geometry>
}
@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,
Collections.singletonMap("dummy_field", value), XContentType.JSON)) {
parser.nextToken(); // start object

View File

@ -298,7 +298,7 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
}
@Override
public ShapeBuilder<?, ?, ?> parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException {
public ShapeBuilder<?, ?, ?> parse(XContentParser parser) throws IOException, ParseException {
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.index.IndexableField;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.mapper.AbstractPointGeometryFieldMapper;
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.query.ShapeQueryPointProcessor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -45,7 +43,10 @@ public class PointFieldMapper extends AbstractPointGeometryFieldMapper<List<Pars
MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {
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.setGeometryQueryBuilder(new ShapeQueryPointProcessor());
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> {
@Override
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,
MultiFields multiFields, Explicit<Boolean> ignoreMalformed,
Explicit<Boolean> ignoreZValue, ParsedPoint nullValue, CopyTo copyTo) {