Skip to content

Commit eb92e01

Browse files
authored
Geo: Refactors libs/geo parser to provide serialization logic as well (#43717)
Enables libs/geo parser to return a geometry format object that can perform both serialization and deserialization functions. This can be useful for ingest nodes that are trying to modify an existing geometry in the source. Relates to #43554
1 parent ce760ec commit eb92e01

File tree

3 files changed

+130
-9
lines changed

3 files changed

+130
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.common.geo;
21+
22+
import org.elasticsearch.common.xcontent.ToXContent;
23+
import org.elasticsearch.common.xcontent.XContentBuilder;
24+
import org.elasticsearch.common.xcontent.XContentParser;
25+
import org.elasticsearch.geo.geometry.Geometry;
26+
27+
import java.io.IOException;
28+
import java.text.ParseException;
29+
30+
/**
31+
* Geometry serializer/deserializer
32+
*/
33+
public interface GeometryFormat {
34+
35+
/**
36+
* Parser JSON representation of a geometry
37+
*/
38+
Geometry fromXContent(XContentParser parser) throws IOException, ParseException;
39+
40+
/**
41+
* Serializes the geometry into its JSON representation
42+
*/
43+
XContentBuilder toXContent(Geometry geometry, XContentBuilder builder, ToXContent.Params params) throws IOException;
44+
45+
}

server/src/main/java/org/elasticsearch/common/geo/GeometryParser.java

+57-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
package org.elasticsearch.common.geo;
2121

2222
import org.elasticsearch.ElasticsearchParseException;
23+
import org.elasticsearch.common.xcontent.ToXContent;
24+
import org.elasticsearch.common.xcontent.XContentBuilder;
2325
import org.elasticsearch.common.xcontent.XContentParser;
2426
import org.elasticsearch.geo.geometry.Geometry;
2527
import org.elasticsearch.geo.utils.GeographyValidator;
@@ -47,14 +49,64 @@ public GeometryParser(boolean rightOrientation, boolean coerce, boolean ignoreZV
4749
/**
4850
* Parses supplied XContent into Geometry
4951
*/
50-
public Geometry parse(XContentParser parser) throws IOException,
51-
ParseException {
52+
public Geometry parse(XContentParser parser) throws IOException, ParseException {
53+
return geometryFormat(parser).fromXContent(parser);
54+
}
55+
56+
/**
57+
* Returns a geometry format object that can parse and then serialize the object back to the same format.
58+
*/
59+
public GeometryFormat geometryFormat(XContentParser parser) {
5260
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
53-
return null;
61+
return new GeometryFormat() {
62+
@Override
63+
public Geometry fromXContent(XContentParser parser) throws IOException {
64+
return null;
65+
}
66+
67+
@Override
68+
public XContentBuilder toXContent(Geometry geometry, XContentBuilder builder, ToXContent.Params params) throws IOException {
69+
if (geometry != null) {
70+
// We don't know the format of the original geometry - so going with default
71+
return GeoJson.toXContent(geometry, builder, params);
72+
} else {
73+
return builder.nullValue();
74+
}
75+
}
76+
};
5477
} else if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
55-
return geoJsonParser.fromXContent(parser);
78+
return new GeometryFormat() {
79+
@Override
80+
public Geometry fromXContent(XContentParser parser) throws IOException {
81+
return geoJsonParser.fromXContent(parser);
82+
}
83+
84+
@Override
85+
public XContentBuilder toXContent(Geometry geometry, XContentBuilder builder, ToXContent.Params params) throws IOException {
86+
if (geometry != null) {
87+
return GeoJson.toXContent(geometry, builder, params);
88+
} else {
89+
return builder.nullValue();
90+
}
91+
}
92+
};
5693
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
57-
return wellKnownTextParser.fromWKT(parser.text());
94+
return new GeometryFormat() {
95+
@Override
96+
public Geometry fromXContent(XContentParser parser) throws IOException, ParseException {
97+
return wellKnownTextParser.fromWKT(parser.text());
98+
}
99+
100+
@Override
101+
public XContentBuilder toXContent(Geometry geometry, XContentBuilder builder, ToXContent.Params params) throws IOException {
102+
if (geometry != null) {
103+
return builder.value(wellKnownTextParser.toWKT(geometry));
104+
} else {
105+
return builder.nullValue();
106+
}
107+
}
108+
};
109+
58110
}
59111
throw new ElasticsearchParseException("shape must be an object consisting of type and coordinates");
60112
}

server/src/test/java/org/elasticsearch/common/geo/GeometryParserTests.java

+28-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
package org.elasticsearch.common.geo;
2121

2222
import org.elasticsearch.ElasticsearchParseException;
23+
import org.elasticsearch.common.Strings;
24+
import org.elasticsearch.common.xcontent.ToXContent;
2325
import org.elasticsearch.common.xcontent.XContentBuilder;
2426
import org.elasticsearch.common.xcontent.XContentFactory;
2527
import org.elasticsearch.common.xcontent.XContentParseException;
@@ -44,7 +46,11 @@ public void testGeoJsonParsing() throws Exception {
4446

4547
try (XContentParser parser = createParser(pointGeoJson)) {
4648
parser.nextToken();
47-
assertEquals(new Point(0, 100), new GeometryParser(true, randomBoolean(), randomBoolean()).parse(parser));
49+
GeometryFormat format = new GeometryParser(true, randomBoolean(), randomBoolean()).geometryFormat(parser);
50+
assertEquals(new Point(0, 100), format.fromXContent(parser));
51+
XContentBuilder newGeoJson = XContentFactory.jsonBuilder();
52+
format.toXContent(new Point(10, 100), newGeoJson, ToXContent.EMPTY_PARAMS);
53+
assertEquals("{\"type\":\"Point\",\"coordinates\":[100.0,10.0]}", Strings.toString(newGeoJson));
4854
}
4955

5056
XContentBuilder pointGeoJsonWithZ = XContentFactory.jsonBuilder()
@@ -77,7 +83,7 @@ public void testGeoJsonParsing() throws Exception {
7783
.endArray()
7884
.endObject();
7985

80-
Polygon p = new Polygon(new LinearRing(new double[] {1d, 1d, 0d, 0d, 1d}, new double[] {100d, 101d, 101d, 100d, 100d}));
86+
Polygon p = new Polygon(new LinearRing(new double[]{1d, 1d, 0d, 0d, 1d}, new double[]{100d, 101d, 101d, 100d, 100d}));
8187
try (XContentParser parser = createParser(polygonGeoJson)) {
8288
parser.nextToken();
8389
// Coerce should automatically close the polygon
@@ -101,7 +107,12 @@ public void testWKTParsing() throws Exception {
101107
parser.nextToken(); // Start object
102108
parser.nextToken(); // Field Name
103109
parser.nextToken(); // Field Value
104-
assertEquals(new Point(0, 100), new GeometryParser(true, randomBoolean(), randomBoolean()).parse(parser));
110+
GeometryFormat format = new GeometryParser(true, randomBoolean(), randomBoolean()).geometryFormat(parser);
111+
assertEquals(new Point(0, 100), format.fromXContent(parser));
112+
XContentBuilder newGeoJson = XContentFactory.jsonBuilder().startObject().field("val");
113+
format.toXContent(new Point(10, 100), newGeoJson, ToXContent.EMPTY_PARAMS);
114+
newGeoJson.endObject();
115+
assertEquals("{\"val\":\"point (100.0 10.0)\"}", Strings.toString(newGeoJson));
105116
}
106117
}
107118

@@ -115,7 +126,20 @@ public void testNullParsing() throws Exception {
115126
parser.nextToken(); // Start object
116127
parser.nextToken(); // Field Name
117128
parser.nextToken(); // Field Value
118-
assertNull(new GeometryParser(true, randomBoolean(), randomBoolean()).parse(parser));
129+
GeometryFormat format = new GeometryParser(true, randomBoolean(), randomBoolean()).geometryFormat(parser);
130+
assertNull(format.fromXContent(parser));
131+
132+
XContentBuilder newGeoJson = XContentFactory.jsonBuilder().startObject().field("val");
133+
// if we serialize non-null value - it should be serialized as geojson
134+
format.toXContent(new Point(10, 100), newGeoJson, ToXContent.EMPTY_PARAMS);
135+
newGeoJson.endObject();
136+
assertEquals("{\"val\":{\"type\":\"Point\",\"coordinates\":[100.0,10.0]}}", Strings.toString(newGeoJson));
137+
138+
newGeoJson = XContentFactory.jsonBuilder().startObject().field("val");
139+
format.toXContent(null, newGeoJson, ToXContent.EMPTY_PARAMS);
140+
newGeoJson.endObject();
141+
assertEquals("{\"val\":null}", Strings.toString(newGeoJson));
142+
119143
}
120144
}
121145

0 commit comments

Comments
 (0)