Skip to content

Commit

Permalink
Consolidate parsing runtime fields from source (#72920)
Browse files Browse the repository at this point in the history
Every runtime field type allows users to omit its script, in which case the field values will be loaded at runtime from _source.

This is implemented by having each field type expose a parse from source script factory that extracts the values and converts them to the appropriate type that can then be emitted.

The extraction logic from source is though always the same, what changes between the different types is the factory type that is needed and how the object values are converted to their appropriate type. This commit moves the common bits to AbstractFieldScript. Especially the conversion from Object to the appropriate type is handy in a specific method as it will be reused to emit multiple fields from a single
script.
  • Loading branch information
javanna committed May 14, 2021
1 parent 34ab0c0 commit 8360a53
Show file tree
Hide file tree
Showing 16 changed files with 238 additions and 187 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,16 @@

public final class BooleanScriptFieldType extends AbstractScriptFieldType<BooleanFieldScript.LeafFactory> {

private static final BooleanFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup) -> (BooleanFieldScript.LeafFactory) ctx -> new BooleanFieldScript
(
field,
params,
lookup,
ctx
) {
@Override
public void execute() {
for (Object v : extractFromSource(field)) {
if (v instanceof Boolean) {
emit((Boolean) v);
} else if (v instanceof String) {
try {
emit(Booleans.parseBoolean((String) v));
} catch (IllegalArgumentException e) {
// ignore
}
}
}
}
};

public static final RuntimeField.Parser PARSER = new RuntimeField.Parser(name ->
new Builder<>(name, BooleanFieldScript.CONTEXT, PARSE_FROM_SOURCE) {
new Builder<>(name, BooleanFieldScript.CONTEXT, BooleanFieldScript.PARSE_FROM_SOURCE) {
@Override
RuntimeField newRuntimeField(BooleanFieldScript.Factory scriptFactory) {
return new BooleanScriptFieldType(name, scriptFactory, getScript(), meta(), this);
}
});

public BooleanScriptFieldType(String name) {
this(name, PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
this(name, BooleanFieldScript.PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
}

BooleanScriptFieldType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,34 +40,12 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

public class DateScriptFieldType extends AbstractScriptFieldType<DateFieldScript.LeafFactory> {

static final DateFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup, formatter) -> (DateFieldScript.LeafFactory) ctx -> new DateFieldScript
(
field,
params,
lookup,
formatter,
ctx
) {
@Override
public void execute() {
for (Object v : extractFromSource(field)) {
try {
emit(formatter.parseMillis(Objects.toString(v)));
} catch (Exception e) {
// ignore
}
}
}
};

public static final RuntimeField.Parser PARSER = new RuntimeField.Parser(name ->
new Builder<>(name, DateFieldScript.CONTEXT, PARSE_FROM_SOURCE) {
new Builder<>(name, DateFieldScript.CONTEXT, DateFieldScript.PARSE_FROM_SOURCE) {
private final FieldMapper.Parameter<String> format = FieldMapper.Parameter.stringParam(
"format",
true,
Expand Down Expand Up @@ -112,7 +90,7 @@ RuntimeField newRuntimeField(DateFieldScript.Factory scriptFactory) {
private final DateMathParser dateMathParser;

public DateScriptFieldType(String name, DateFormatter dateTimeFormatter) {
this(name, PARSE_FROM_SOURCE, dateTimeFormatter, null, Collections.emptyMap(), (builder, params) -> {
this(name, DateFieldScript.PARSE_FROM_SOURCE, dateTimeFormatter, null, Collections.emptyMap(), (builder, params) -> {
if (DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.pattern().equals(dateTimeFormatter.pattern()) == false) {
builder.field("format", dateTimeFormatter.pattern());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,16 @@

public final class DoubleScriptFieldType extends AbstractScriptFieldType<DoubleFieldScript.LeafFactory> {

static final DoubleFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup) -> (DoubleFieldScript.LeafFactory) ctx -> new DoubleFieldScript
(
field,
params,
lookup,
ctx
) {
@Override
public void execute() {
for (Object v : extractFromSource(field)) {
if (v instanceof Number) {
emit(((Number) v).doubleValue());
} else if (v instanceof String) {
try {
emit(Double.parseDouble((String) v));
} catch (NumberFormatException e) {
// ignore
}
}
}
}
};

public static final RuntimeField.Parser PARSER = new RuntimeField.Parser(name ->
new Builder<>(name, DoubleFieldScript.CONTEXT, PARSE_FROM_SOURCE) {
new Builder<>(name, DoubleFieldScript.CONTEXT, DoubleFieldScript.PARSE_FROM_SOURCE) {
@Override
RuntimeField newRuntimeField(DoubleFieldScript.Factory scriptFactory) {
return new DoubleScriptFieldType(name, scriptFactory, getScript(), meta(), this);
}
});

public DoubleScriptFieldType(String name) {
this(name, PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
this(name, DoubleFieldScript.PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
}

DoubleScriptFieldType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.elasticsearch.common.time.DateMathParser;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.index.fielddata.GeoPointScriptFieldData;
import org.elasticsearch.index.query.SearchExecutionContext;
Expand All @@ -32,53 +31,13 @@

import java.time.ZoneId;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

public final class GeoPointScriptFieldType extends AbstractScriptFieldType<GeoPointFieldScript.LeafFactory> implements GeoShapeQueryable {

private static final GeoPointFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup) -> (GeoPointFieldScript.LeafFactory) ctx -> new GeoPointFieldScript
(
field,
params,
lookup,
ctx
) {
private final GeoPoint scratch = new GeoPoint();

@Override
public void execute() {
try {
Object value = XContentMapValues.extractValue(field, leafSearchLookup.source().source());
if (value instanceof List<?>) {
List<?> values = (List<?>) value;
if (values.size() > 0 && values.get(0) instanceof Number) {
parsePoint(value);
} else {
for (Object point : values) {
parsePoint(point);
}
}
} else {
parsePoint(value);
}
} catch (Exception e) {
// ignore
}
}

private void parsePoint(Object point) {
if (point != null) {
GeoUtils.parseGeoPoint(point, scratch, true);
emit(scratch.lat(), scratch.lon());
}
}
};

public static final RuntimeField.Parser PARSER = new RuntimeField.Parser(name ->
new Builder<>(name, GeoPointFieldScript.CONTEXT, PARSE_FROM_SOURCE) {
new Builder<>(name, GeoPointFieldScript.CONTEXT, GeoPointFieldScript.PARSE_FROM_SOURCE) {
@Override
RuntimeField newRuntimeField(GeoPointFieldScript.Factory scriptFactory) {
return new GeoPointScriptFieldType(name, scriptFactory, getScript(), meta(), this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,8 @@

public final class IpScriptFieldType extends AbstractScriptFieldType<IpFieldScript.LeafFactory> {

private static final IpFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup) -> (IpFieldScript.LeafFactory) ctx -> new IpFieldScript
(
field,
params,
lookup,
ctx
) {
@Override
public void execute() {
for (Object v : extractFromSource(field)) {
if (v instanceof String) {
try {
emit((String) v);
} catch (Exception e) {
// ignore parsing exceptions
}
}
}
}
};

public static final RuntimeField.Parser PARSER = new RuntimeField.Parser(name ->
new Builder<>(name, IpFieldScript.CONTEXT, PARSE_FROM_SOURCE) {
new Builder<>(name, IpFieldScript.CONTEXT, IpFieldScript.PARSE_FROM_SOURCE) {
@Override
RuntimeField newRuntimeField(IpFieldScript.Factory scriptFactory) {
return new IpScriptFieldType(name, scriptFactory, getScript(), meta(), this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,34 +41,16 @@

public final class KeywordScriptFieldType extends AbstractScriptFieldType<StringFieldScript.LeafFactory> {

private static final StringFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup) -> (StringFieldScript.LeafFactory) ctx -> new StringFieldScript
(
field,
params,
lookup,
ctx
) {
@Override
public void execute() {
for (Object v : extractFromSource(field)) {
if (v != null) {
emit(v.toString());
}
}
}
};

public static final RuntimeField.Parser PARSER = new RuntimeField.Parser(name ->
new Builder<>(name, StringFieldScript.CONTEXT, PARSE_FROM_SOURCE) {
new Builder<>(name, StringFieldScript.CONTEXT, StringFieldScript.PARSE_FROM_SOURCE) {
@Override
RuntimeField newRuntimeField(StringFieldScript.Factory scriptFactory) {
return new KeywordScriptFieldType(name, scriptFactory, getScript(), meta(), this);
}
});

public KeywordScriptFieldType(String name) {
this(name, PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
this(name, StringFieldScript.PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
}

KeywordScriptFieldType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,36 +34,16 @@

public final class LongScriptFieldType extends AbstractScriptFieldType<LongFieldScript.LeafFactory> {

static final LongFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup) -> (LongFieldScript.LeafFactory) ctx -> new LongFieldScript
(
field,
params,
lookup,
ctx
) {
@Override
public void execute() {
for (Object v : extractFromSource(field)) {
try {
emit(NumberFieldMapper.NumberType.objectToLong(v, true));
} catch (Exception e) {
// ignore;
}
}
}
};

public static final RuntimeField.Parser PARSER = new RuntimeField.Parser(name ->
new Builder<>(name, LongFieldScript.CONTEXT, PARSE_FROM_SOURCE) {
new Builder<>(name, LongFieldScript.CONTEXT, LongFieldScript.PARSE_FROM_SOURCE) {
@Override
RuntimeField newRuntimeField(LongFieldScript.Factory scriptFactory) {
return new LongScriptFieldType(name, scriptFactory, getScript(), meta(), this);
}
});

public LongScriptFieldType(String name) {
this(name, PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
this(name, LongFieldScript.PARSE_FROM_SOURCE, null, Collections.emptyMap(), (builder, params) -> builder);
}

LongScriptFieldType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,18 @@ public final Map<String, ScriptDocValues<?>> getDoc() {
return leafSearchLookup.doc();
}

protected final List<Object> extractFromSource(String path) {
protected List<Object> extractFromSource(String path) {
return XContentMapValues.extractRawValues(path, leafSearchLookup.source().source());
}

protected abstract void emitFromObject(Object v);

protected final void emitFromSource() {
for (Object v : extractFromSource(fieldName)) {
emitFromObject(v);
}
}

/**
* Check if the we can add another value to the list of values.
* @param currentSize the current size of the list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.elasticsearch.script;

import org.apache.lucene.index.LeafReaderContext;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.search.lookup.SearchLookup;

import java.util.Map;
Expand All @@ -18,6 +19,20 @@ public abstract class BooleanFieldScript extends AbstractFieldScript {

public static final ScriptContext<Factory> CONTEXT = newContext("boolean_field", Factory.class);

public static final BooleanFieldScript.Factory PARSE_FROM_SOURCE
= (field, params, lookup) -> (BooleanFieldScript.LeafFactory) ctx -> new BooleanFieldScript
(
field,
params,
lookup,
ctx
) {
@Override
public void execute() {
emitFromSource();
}
};

@SuppressWarnings("unused")
public static final String[] PARAMETERS = {};

Expand Down Expand Up @@ -68,6 +83,18 @@ public final int falses() {
return falses;
}

protected final void emitFromObject(Object v) {
if (v instanceof Boolean) {
emit((Boolean) v);
} else if (v instanceof String) {
try {
emit(Booleans.parseBoolean((String) v));
} catch (IllegalArgumentException e) {
// ignore
}
}
}

public final void emit(boolean v) {
if (v) {
trues++;
Expand Down

0 comments on commit 8360a53

Please sign in to comment.