Skip to content

Commit

Permalink
Refactor PointParser to not take FieldMapper as a parameter (#62950)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
romseygeek committed Sep 28, 2020
1 parent 4d43fa8 commit a3ba241
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public abstract static class Parser<Parsed> {
* 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.
Expand All @@ -106,14 +106,14 @@ public abstract static class Parser<Parsed> {
* 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) {
Expand Down Expand Up @@ -188,7 +188,7 @@ public ValueFetcher valueFetcher(MapperService mapperService, SearchLookup searc

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
Expand Down Expand Up @@ -340,7 +340,7 @@ public void parse(ParseContext context) throws IOException {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

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;
Expand All @@ -34,9 +35,11 @@
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;

Expand Down Expand Up @@ -154,8 +157,6 @@ public ParsedPoint nullValue() {
return nullValue;
}

protected abstract ParsedPoint newParsedPoint();

/** represents a Point that has been parsed by {@link PointParser} */
public interface ParsedPoint {
void validate(String fieldName);
Expand All @@ -167,87 +168,80 @@ default boolean isNormalizable(double coord) {
}
}

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;

public PointParser() {
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(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())));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
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;
Expand Down Expand Up @@ -75,7 +74,10 @@ public GeoPointFieldMapper build(BuilderContext context, String simpleName, Fiel
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);
Expand Down Expand Up @@ -105,15 +107,6 @@ protected ParsedGeoPoint parseNullValue(Object nullValue, boolean ignoreZValue,
}
}

/**
* 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) {
Expand Down Expand Up @@ -166,11 +159,6 @@ public GeoPointFieldType fieldType() {
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);
Expand Down Expand Up @@ -251,12 +239,8 @@ public boolean equals(Object other) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public GeoShapeParser(GeometryParser geometryParser) {
}

@Override
public Geometry parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException {
public Geometry parse(XContentParser parser) throws IOException, ParseException {
return geometryParser.parse(parser);
}

Expand All @@ -51,7 +51,7 @@ public Object format(Geometry value, String format) {
}

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ private LegacyGeoShapeParser() {
}

@Override
public ShapeBuilder<?, ?, ?> parse(XContentParser parser, AbstractGeometryFieldMapper mapper) throws IOException, ParseException {
public ShapeBuilder<?, ?, ?> parse(XContentParser parser) throws IOException, ParseException {
return ShapeParser.parse(parser);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
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;
Expand All @@ -20,7 +19,6 @@
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;
Expand All @@ -45,7 +43,10 @@ public PointFieldMapper build(BuilderContext context, String simpleName, FieldTy
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,
Expand All @@ -54,11 +55,6 @@ public PointFieldMapper build(BuilderContext context, String simpleName, FieldTy

}

@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) {
Expand All @@ -81,15 +77,6 @@ protected ParsedPoint parseNullValue(Object nullValue, boolean ignoreZValue, boo
}
}

/**
* 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) {
Expand Down

0 comments on commit a3ba241

Please sign in to comment.