Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.geo.geometry.ShapeType;
import org.elasticsearch.geo.utils.Geohash;
import org.elasticsearch.geo.utils.WellKnownText;
import org.elasticsearch.search.SearchHit;

import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

Expand Down Expand Up @@ -68,8 +69,12 @@ public static ExtractedField newTimeField(String name, ExtractionMethod extracti
return new TimeField(name, extractionMethod);
}

public static ExtractedField newGeoField(String alias, String name, ExtractionMethod extractionMethod) {
return new GeoField(alias, name, extractionMethod);
public static ExtractedField newGeoShapeField(String alias, String name, ExtractionMethod extractionMethod) {
return new GeoShapeField(alias, name, extractionMethod);
}

public static ExtractedField newGeoPointField(String alias, String name, ExtractionMethod extractionMethod) {
return new GeoPointField(alias, name, extractionMethod);
}

public static ExtractedField newField(String name, ExtractionMethod extractionMethod) {
Expand Down Expand Up @@ -105,40 +110,27 @@ public Object[] value(SearchHit hit) {
}
}

private static class GeoField extends ExtractedField {
private static class GeoShapeField extends FromSource {
private static final WellKnownText wkt = new WellKnownText();

private final ExtractedField internalExtractor;
private final WellKnownText wkt = new WellKnownText();

GeoField(String alias, String name, ExtractionMethod extractionMethod) {
GeoShapeField(String alias, String name, ExtractionMethod extractionMethod) {
super(alias, name, extractionMethod);
internalExtractor = extractionMethod.equals(ExtractionMethod.SOURCE) ?
new FromSource(alias, name, extractionMethod) :
new FromFields(alias, name, extractionMethod);
}

@Override
public Object[] value(SearchHit hit) {
Object[] value = internalExtractor.value(hit);
if (value.length == 2 && internalExtractor.getExtractionMethod().equals(ExtractionMethod.SOURCE)) { // geo_point as array
return new Object[] {value[0] + "," + value[1]};
}
Object[] value = super.value(hit);
if (value.length != 1) {
throw new IllegalStateException("Unexpected value count for a geo point field: " + value);
throw new IllegalStateException("Unexpected values for a geo_shape field: " + Arrays.toString(value));
}
if (value[0] instanceof String) {
value[0] = handleString((String) value[0]);
} else if(value[0] instanceof Map<?, ?>) {
} else if (value[0] instanceof Map<?, ?>) {
@SuppressWarnings("unchecked")
Map<String, Object> geoObject = (Map<String, Object>) value[0];
value[0] = handleObject(geoObject);
} else if(value[0] instanceof List<?>) {
@SuppressWarnings("unchecked")
List<Double> coordinates = (List<Double>) value[0];
assert coordinates.size() == 2;
value[0] = coordinates.get(0) + "," + coordinates.get(1);
} else {
throw new IllegalStateException("Unexpected value type for a geo point field: " + value[0].getClass());
throw new IllegalStateException("Unexpected value type for a geo_shape field: " + value[0].getClass());
}
return value;
}
Expand All @@ -148,33 +140,59 @@ private String handleString(String geoString) {
if (geoString.startsWith("POINT")) { // Entry is of the form "POINT (-77.03653 38.897676)"
Geometry geometry = wkt.fromWKT(geoString);
if (geometry.type() != ShapeType.POINT) {
throw new IllegalArgumentException("Unexpected non-point geo type: " + geometry.type().name());
throw new IllegalArgumentException("Unexpected non-point geo_shape type: " + geometry.type().name());
}
Point pt = ((Point)geometry);
return pt.getLat() + "," + pt.getLon();
} else if (geoString.contains(",")) { // Entry is of the form "38.897676, -77.03653"
return geoString.replace(" ", "");
} else { // This may be a geohash, attempt to decode
Point pt = Geohash.toPoint(geoString);
return pt.getLat() + "," + pt.getLon();
} else {
throw new IllegalArgumentException("Unexpected value for a geo_shape field: " + geoString);
}
} catch (IOException | ParseException ex) {
throw new IllegalArgumentException("Unexpected value for a geo field: " + geoString);
throw new IllegalArgumentException("Unexpected value for a geo_shape field: " + geoString);
}
}

private String handleObject(Map<String, Object> geoObject) {
if ("point".equals(geoObject.get("type"))) { // geo_shape
String geoType = (String) geoObject.get("type");
if (geoType != null && "point".equals(geoType.toLowerCase(Locale.ROOT))) {
@SuppressWarnings("unchecked")
List<Double> coordinates = (List<Double>)geoObject.get("coordinates");
if (coordinates == null || coordinates.size() != 2) {
throw new IllegalArgumentException("Invalid coordinates for geo_shape point: " + geoObject);
}
return coordinates.get(1) + "," + coordinates.get(0);
} else if (geoObject.containsKey("lat") && geoObject.containsKey("lon")) { // geo_point
return geoObject.get("lat") + "," + geoObject.get("lon");
List<Double> coordinates = (List<Double>) geoObject.get("coordinates");
if (coordinates == null || coordinates.size() != 2) {
throw new IllegalArgumentException("Invalid coordinates for geo_shape point: " + geoObject);
}
return coordinates.get(1) + "," + coordinates.get(0);
} else {
throw new IllegalArgumentException("Unexpected value for a geo_shape field: " + geoObject);
}
}

}

private static class GeoPointField extends FromFields {

GeoPointField(String alias, String name, ExtractionMethod extractionMethod) {
super(alias, name, extractionMethod);
}

@Override
public Object[] value(SearchHit hit) {
Object[] value = super.value(hit);
if (value.length != 1) {
throw new IllegalStateException("Unexpected values for a geo_point field: " + Arrays.toString(value));
}
if (value[0] instanceof String) {
value[0] = handleString((String) value[0]);
} else {
throw new IllegalStateException("Unexpected value type for a geo_point field: " + value[0].getClass());
}
return value;
}

private String handleString(String geoString) {
if (geoString.contains(",")) { // Entry is of the form "38.897676, -77.03653"
return geoString.replace(" ", "");
} else {
throw new IllegalArgumentException("Unexpected value for a geo field: " + geoObject);
throw new IllegalArgumentException("Unexpected value for a geo_point field: " + geoString);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,14 @@ protected ExtractedField detect(String field) {
: ExtractedField.ExtractionMethod.SOURCE;
}
}
if (isFieldOfType(field, "geo_point") || isFieldOfType(field, "geo_shape")) {
return ExtractedField.newGeoField(field, internalField, method);
if (isFieldOfType(field, "geo_point")) {
if (method != ExtractedField.ExtractionMethod.DOC_VALUE) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know I suggested checking this here and I'm sorry. I think this belongs in GeoPointField's extractor.

throw new IllegalArgumentException("cannot use [geo_point] field with disabled doc values");
}
return ExtractedField.newGeoPointField(field, internalField, method);
}
if (isFieldOfType(field, "geo_shape")) {
return ExtractedField.newGeoShapeField(field, internalField, method);
}
return ExtractedField.newField(field, internalField, method);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
*/
package org.elasticsearch.xpack.ml.datafeed.extractor.fields;

import org.elasticsearch.geo.utils.Geohash;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.ml.test.SearchHitBuilder;

import java.util.Arrays;

import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.startsWith;

Expand Down Expand Up @@ -68,32 +66,10 @@ public void testGeoPoint() {
double lat = 38.897676;
double lon = -77.03653;
String[] expected = new String[] {lat + "," + lon};
// object format
SearchHit hit = new SearchHitBuilder(42).setSource("{\"geo\":{\"lat\":" + lat + ", \"lon\": " + lon + "}}").build();

ExtractedField geo = ExtractedField.newGeoField("geo", "geo", ExtractedField.ExtractionMethod.SOURCE);
assertThat(geo.value(hit), equalTo(expected));

// double array
hit = new SearchHitBuilder(42).setSource("{\"geo\": [" + lat + ", " + lon + "]}").build();
assertThat(geo.value(hit), equalTo(expected));

// comma delimited string
hit = new SearchHitBuilder(42).setSource("{\"geo\": \"" + lat + "," + lon + "\"}").build();
assertThat(geo.value(hit), equalTo(expected));

// geohash
hit = new SearchHitBuilder(42).setSource("{\"geo\": \"" + Geohash.stringEncode(lon, lat) + "\"}").build();
Object[] returned = geo.value(hit);
// Geohash encode/decodes may change the double values slightly
assertThat(returned.length, equalTo(1));
String[] doubles = ((String)returned[0]).split(",");
assertThat(Double.valueOf(doubles[0]), closeTo(lat, 0.0001));
assertThat(Double.valueOf(doubles[1]), closeTo(lon, 0.0001));

// doc_value field
geo = ExtractedField.newGeoField("geo", "geo", ExtractedField.ExtractionMethod.DOC_VALUE);
hit = new SearchHitBuilder(42).addField("geo", lat + ", " + lon).build();
ExtractedField geo = ExtractedField.newGeoPointField("geo", "geo", ExtractedField.ExtractionMethod.DOC_VALUE);
SearchHit hit = new SearchHitBuilder(42).addField("geo", lat + ", " + lon).build();
assertThat(geo.value(hit), equalTo(expected));
}

Expand All @@ -105,12 +81,12 @@ public void testGeoShape() {
SearchHit hit = new SearchHitBuilder(42)
.setSource("{\"geo\":{\"type\":\"point\", \"coordinates\": [" + lon + ", " + lat + "]}}")
.build();
ExtractedField geo = ExtractedField.newGeoField("geo", "geo", ExtractedField.ExtractionMethod.SOURCE);
ExtractedField geo = ExtractedField.newGeoShapeField("geo", "geo", ExtractedField.ExtractionMethod.SOURCE);
assertThat(geo.value(hit), equalTo(expected));

// WKT format
hit = new SearchHitBuilder(42).setSource("{\"geo\":\"POINT ("+ lon + " " + lat + ")\"}").build();
geo = ExtractedField.newGeoField("geo", "geo", ExtractedField.ExtractionMethod.SOURCE);
geo = ExtractedField.newGeoShapeField("geo", "geo", ExtractedField.ExtractionMethod.SOURCE);
assertThat(geo.value(hit), equalTo(expected));
}

Expand Down