|
@@ -11,30 +11,39 @@ import org.apache.lucene.document.LatLonDocValuesField;
|
|
|
import org.apache.lucene.document.LatLonPoint;
|
|
|
import org.apache.lucene.document.StoredField;
|
|
|
import org.apache.lucene.geo.LatLonGeometry;
|
|
|
+import org.apache.lucene.index.LeafReaderContext;
|
|
|
import org.apache.lucene.search.IndexOrDocValuesQuery;
|
|
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
|
|
import org.apache.lucene.search.Query;
|
|
|
import org.elasticsearch.ElasticsearchParseException;
|
|
|
import org.elasticsearch.common.Explicit;
|
|
|
+import org.elasticsearch.common.geo.GeoJsonGeometryFormat;
|
|
|
import org.elasticsearch.common.geo.GeoPoint;
|
|
|
import org.elasticsearch.common.geo.GeoShapeUtils;
|
|
|
import org.elasticsearch.common.geo.GeoUtils;
|
|
|
+import org.elasticsearch.common.geo.GeometryFormat;
|
|
|
+import org.elasticsearch.common.geo.GeometryParser;
|
|
|
import org.elasticsearch.common.geo.ShapeRelation;
|
|
|
import org.elasticsearch.common.unit.DistanceUnit;
|
|
|
import org.elasticsearch.geometry.Geometry;
|
|
|
import org.elasticsearch.geometry.Point;
|
|
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
|
|
import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointIndexFieldData;
|
|
|
-import org.elasticsearch.index.mapper.GeoPointFieldMapper.ParsedGeoPoint;
|
|
|
import org.elasticsearch.index.query.SearchExecutionContext;
|
|
|
+import org.elasticsearch.script.GeoPointFieldScript;
|
|
|
+import org.elasticsearch.script.Script;
|
|
|
+import org.elasticsearch.script.ScriptCompiler;
|
|
|
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
|
|
|
+import org.elasticsearch.search.lookup.FieldValues;
|
|
|
import org.elasticsearch.search.lookup.SearchLookup;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
+import java.io.UncheckedIOException;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
|
/**
|
|
@@ -42,7 +51,7 @@ import java.util.function.Supplier;
|
|
|
*
|
|
|
* Uses lucene 6 LatLonPoint encoding
|
|
|
*/
|
|
|
-public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<ParsedGeoPoint> {
|
|
|
+public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<GeoPoint> {
|
|
|
|
|
|
public static final String CONTENT_TYPE = "geo_point";
|
|
|
|
|
@@ -58,20 +67,27 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
final Parameter<Boolean> indexed = Parameter.indexParam(m -> builder(m).indexed.get(), true);
|
|
|
final Parameter<Boolean> hasDocValues = Parameter.docValuesParam(m -> builder(m).hasDocValues.get(), true);
|
|
|
final Parameter<Boolean> stored = Parameter.storeParam(m -> builder(m).stored.get(), false);
|
|
|
+ private final Parameter<Script> script = Parameter.scriptParam(m -> builder(m).script.get());
|
|
|
+ private final Parameter<String> onScriptError = Parameter.onScriptErrorParam(m -> builder(m).onScriptError.get(), script);
|
|
|
final Parameter<Map<String, String>> meta = Parameter.metaParam();
|
|
|
|
|
|
- public Builder(String name, boolean ignoreMalformedByDefault) {
|
|
|
+ private final ScriptCompiler scriptCompiler;
|
|
|
+
|
|
|
+ public Builder(String name, ScriptCompiler scriptCompiler, boolean ignoreMalformedByDefault) {
|
|
|
super(name);
|
|
|
this.ignoreMalformed = ignoreMalformedParam(m -> builder(m).ignoreMalformed.get(), ignoreMalformedByDefault);
|
|
|
this.nullValue = nullValueParam(
|
|
|
m -> builder(m).nullValue.get(),
|
|
|
(n, c, o) -> parseNullValue(o, ignoreZValue.get().value(), ignoreMalformed.get().value()),
|
|
|
() -> null).acceptsNull();
|
|
|
+ this.scriptCompiler = Objects.requireNonNull(scriptCompiler);
|
|
|
+ this.script.precludesParameters(nullValue, ignoreMalformed, ignoreZValue);
|
|
|
+ addScriptValidation(script, indexed, hasDocValues);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected List<Parameter<?>> getParameters() {
|
|
|
- return Arrays.asList(hasDocValues, indexed, stored, ignoreMalformed, ignoreZValue, nullValue, meta);
|
|
|
+ return Arrays.asList(hasDocValues, indexed, stored, ignoreMalformed, ignoreZValue, nullValue, script, onScriptError, meta);
|
|
|
}
|
|
|
|
|
|
public Builder docValues(boolean hasDocValues) {
|
|
@@ -98,6 +114,17 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
return point;
|
|
|
}
|
|
|
|
|
|
+ private FieldValues<GeoPoint> scriptValues() {
|
|
|
+ if (this.script.get() == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ GeoPointFieldScript.Factory factory = scriptCompiler.compile(this.script.get(), GeoPointFieldScript.CONTEXT);
|
|
|
+ return factory == null ? null : (lookup, ctx, doc, consumer) -> factory
|
|
|
+ .newFactory(name, script.get().getParams(), lookup)
|
|
|
+ .newInstance(ctx)
|
|
|
+ .runGeoPointForDoc(doc, consumer);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public FieldMapper build(ContentPath contentPath) {
|
|
|
Parser<ParsedGeoPoint> geoParser = new PointParser<>(
|
|
@@ -110,17 +137,28 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
(ParsedGeoPoint) nullValue.get(),
|
|
|
ignoreZValue.get().value(),
|
|
|
ignoreMalformed.get().value());
|
|
|
- GeoPointFieldType ft
|
|
|
- = new GeoPointFieldType(buildFullName(contentPath), indexed.get(), stored.get(), hasDocValues.get(), geoParser, meta.get());
|
|
|
- return new GeoPointFieldMapper(name, ft, multiFieldsBuilder.build(this, contentPath),
|
|
|
- copyTo.build(), geoParser, this);
|
|
|
+ GeoPointFieldType ft = new GeoPointFieldType(
|
|
|
+ buildFullName(contentPath),
|
|
|
+ indexed.get(),
|
|
|
+ stored.get(),
|
|
|
+ hasDocValues.get(),
|
|
|
+ geoParser,
|
|
|
+ scriptValues(),
|
|
|
+ meta.get());
|
|
|
+ if (this.script.get() == null) {
|
|
|
+ return new GeoPointFieldMapper(name, ft, multiFieldsBuilder.build(this, contentPath),
|
|
|
+ copyTo.build(), geoParser, this);
|
|
|
+ }
|
|
|
+ return new GeoPointFieldMapper(name, ft, geoParser, this);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- public static TypeParser PARSER = new TypeParser((n, c) -> new Builder(n, IGNORE_MALFORMED_SETTING.get(c.getSettings())));
|
|
|
+ public static TypeParser PARSER
|
|
|
+ = new TypeParser((n, c) -> new Builder(n, c.scriptCompiler(), IGNORE_MALFORMED_SETTING.get(c.getSettings())));
|
|
|
|
|
|
private final Builder builder;
|
|
|
+ private final FieldValues<GeoPoint> scriptValues;
|
|
|
|
|
|
public GeoPointFieldMapper(String simpleName, MappedFieldType mappedFieldType,
|
|
|
MultiFields multiFields, CopyTo copyTo,
|
|
@@ -130,15 +168,23 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
builder.ignoreMalformed.get(), builder.ignoreZValue.get(), builder.nullValue.get(),
|
|
|
copyTo, parser);
|
|
|
this.builder = builder;
|
|
|
+ this.scriptValues = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public GeoPointFieldMapper(String simpleName, MappedFieldType mappedFieldType,
|
|
|
+ Parser<ParsedGeoPoint> parser, Builder builder) {
|
|
|
+ super(simpleName, mappedFieldType, MultiFields.empty(), CopyTo.empty(), parser, builder.onScriptError.get());
|
|
|
+ this.builder = builder;
|
|
|
+ this.scriptValues = builder.scriptValues();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public FieldMapper.Builder getMergeBuilder() {
|
|
|
- return new Builder(simpleName(), builder.ignoreMalformed.getDefaultValue().value()).init(this);
|
|
|
+ return new Builder(simpleName(), builder.scriptCompiler, builder.ignoreMalformed.getDefaultValue().value()).init(this);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- protected void index(ParseContext context, ParsedGeoPoint geometry) throws IOException {
|
|
|
+ protected void index(ParseContext context, GeoPoint geometry) throws IOException {
|
|
|
if (fieldType().isSearchable()) {
|
|
|
context.doc().add(new LatLonPoint(fieldType().name(), geometry.lat(), geometry.lon()));
|
|
|
}
|
|
@@ -154,6 +200,17 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
multiFields.parse(this, context.createExternalValueContext(geometry.geohash()));
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ protected void indexScriptValues(SearchLookup searchLookup, LeafReaderContext readerContext, int doc, ParseContext parseContext) {
|
|
|
+ this.scriptValues.valuesForDoc(searchLookup, readerContext, doc, point -> {
|
|
|
+ try {
|
|
|
+ index(parseContext, point);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new UncheckedIOException(e); // only thrown by MultiFields which is always null
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
protected String contentType() {
|
|
|
return CONTENT_TYPE;
|
|
@@ -161,13 +218,17 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
|
|
|
public static class GeoPointFieldType extends AbstractGeometryFieldType implements GeoShapeQueryable {
|
|
|
|
|
|
+ private static final GeometryParser PARSER = new GeometryParser(true, true, true);
|
|
|
+ private final FieldValues<GeoPoint> scriptValues;
|
|
|
+
|
|
|
private GeoPointFieldType(String name, boolean indexed, boolean stored, boolean hasDocValues,
|
|
|
- Parser<?> parser, Map<String, String> meta) {
|
|
|
+ Parser<ParsedGeoPoint> parser, FieldValues<GeoPoint> scriptValues, Map<String, String> meta) {
|
|
|
super(name, indexed, stored, hasDocValues, true, parser, meta);
|
|
|
+ this.scriptValues = scriptValues;
|
|
|
}
|
|
|
|
|
|
public GeoPointFieldType(String name) {
|
|
|
- this(name, true, false, true, null, Collections.emptyMap());
|
|
|
+ this(name, true, false, true, null, null, Collections.emptyMap());
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -175,6 +236,19 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
return CONTENT_TYPE;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public ValueFetcher valueFetcher(SearchExecutionContext context, String format) {
|
|
|
+ if (scriptValues == null) {
|
|
|
+ return super.valueFetcher(context, format);
|
|
|
+ }
|
|
|
+ String geoFormat = format != null ? format : GeoJsonGeometryFormat.NAME;
|
|
|
+ GeometryFormat<Geometry> geometryFormat = PARSER.geometryFormat(geoFormat);
|
|
|
+ return FieldValues.valueFetcher(scriptValues, v -> {
|
|
|
+ GeoPoint p = (GeoPoint) v;
|
|
|
+ return geometryFormat.toXContentAsObject(new Point(p.lon(), p.lat()));
|
|
|
+ }, context);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public Query geoShapeQuery(Geometry shape, String fieldName, ShapeRelation relation, SearchExecutionContext context) {
|
|
|
final LatLonGeometry[] luceneGeometries = GeoShapeUtils.toLuceneGeometry(fieldName, context, shape, relation);
|
|
@@ -270,4 +344,5 @@ public class GeoPointFieldMapper extends AbstractPointGeometryFieldMapper<Parsed
|
|
|
return super.hashCode();
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|