浏览代码

Consolidate parsing runtime fields from source (#72920)

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.
Luca Cavanna 4 年之前
父节点
当前提交
8360a53e7b

+ 2 - 26
server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptFieldType.java

@@ -33,32 +33,8 @@ import java.util.function.Supplier;
 
 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);
@@ -66,7 +42,7 @@ public final class BooleanScriptFieldType extends AbstractScriptFieldType<Boolea
         });
 
     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(

+ 2 - 24
server/src/main/java/org/elasticsearch/index/mapper/DateScriptFieldType.java

@@ -40,34 +40,12 @@ import java.util.Collections;
 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,
@@ -112,7 +90,7 @@ public class DateScriptFieldType extends AbstractScriptFieldType<DateFieldScript
     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());
             }

+ 2 - 26
server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptFieldType.java

@@ -34,32 +34,8 @@ import java.util.function.Supplier;
 
 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);
@@ -67,7 +43,7 @@ public final class DoubleScriptFieldType extends AbstractScriptFieldType<DoubleF
         });
 
     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(

+ 1 - 42
server/src/main/java/org/elasticsearch/index/mapper/GeoPointScriptFieldType.java

@@ -19,7 +19,6 @@ import org.elasticsearch.common.geo.ShapeRelation;
 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;
@@ -32,53 +31,13 @@ import org.elasticsearch.search.runtime.GeoPointScriptFieldGeoShapeQuery;
 
 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);

+ 1 - 23
server/src/main/java/org/elasticsearch/index/mapper/IpScriptFieldType.java

@@ -42,30 +42,8 @@ import java.util.function.Supplier;
 
 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);

+ 2 - 20
server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptFieldType.java

@@ -41,26 +41,8 @@ import static java.util.stream.Collectors.toSet;
 
 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);
@@ -68,7 +50,7 @@ public final class KeywordScriptFieldType extends AbstractScriptFieldType<String
         });
 
     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(

+ 2 - 22
server/src/main/java/org/elasticsearch/index/mapper/LongScriptFieldType.java

@@ -34,28 +34,8 @@ import java.util.function.Supplier;
 
 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);
@@ -63,7 +43,7 @@ public final class LongScriptFieldType extends AbstractScriptFieldType<LongField
         });
 
     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(

+ 9 - 1
server/src/main/java/org/elasticsearch/script/AbstractFieldScript.java

@@ -102,10 +102,18 @@ public abstract class AbstractFieldScript {
         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

+ 27 - 0
server/src/main/java/org/elasticsearch/script/BooleanFieldScript.java

@@ -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;
@@ -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 = {};
 
@@ -68,6 +83,18 @@ public abstract class BooleanFieldScript extends AbstractFieldScript {
         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++;

+ 25 - 0
server/src/main/java/org/elasticsearch/script/DateFieldScript.java

@@ -13,10 +13,26 @@ import org.elasticsearch.common.time.DateFormatter;
 import org.elasticsearch.search.lookup.SearchLookup;
 
 import java.util.Map;
+import java.util.Objects;
 
 public abstract class DateFieldScript extends AbstractLongFieldScript {
     public static final ScriptContext<Factory> CONTEXT = newContext("date_field", Factory.class);
 
+    public 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() {
+            emitFromSource();
+        }
+    };
+
     @SuppressWarnings("unused")
     public static final String[] PARAMETERS = {};
 
@@ -41,6 +57,15 @@ public abstract class DateFieldScript extends AbstractLongFieldScript {
         this.formatter = formatter;
     }
 
+    @Override
+    protected void emitFromObject(Object v) {
+        try {
+            emit(formatter.parseMillis(Objects.toString(v)));
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
     public static class Emit {
         private final DateFieldScript script;
 

+ 27 - 0
server/src/main/java/org/elasticsearch/script/DoubleFieldScript.java

@@ -18,6 +18,20 @@ import java.util.function.DoubleConsumer;
 public abstract class DoubleFieldScript extends AbstractFieldScript {
     public static final ScriptContext<Factory> CONTEXT = newContext("double_field", Factory.class);
 
+    public static final DoubleFieldScript.Factory PARSE_FROM_SOURCE
+        = (field, params, lookup) -> (DoubleFieldScript.LeafFactory) ctx -> new DoubleFieldScript
+        (
+            field,
+            params,
+            lookup,
+            ctx
+        ) {
+        @Override
+        public void execute() {
+            emitFromSource();
+        }
+    };
+
     @SuppressWarnings("unused")
     public static final String[] PARAMETERS = {};
 
@@ -72,6 +86,19 @@ public abstract class DoubleFieldScript extends AbstractFieldScript {
         return count;
     }
 
+    @Override
+    protected void emitFromObject(Object v) {
+        if (v instanceof Number) {
+            emit(((Number) v).doubleValue());
+        } else if (v instanceof String) {
+            try {
+                emit(Double.parseDouble((String) v));
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+    }
+
     public final void emit(double v) {
         checkMaxSize(count);
         if (values.length < count + 1) {

+ 66 - 1
server/src/main/java/org/elasticsearch/script/GeoPointFieldScript.java

@@ -12,8 +12,12 @@ import org.apache.lucene.document.LatLonDocValuesField;
 import org.apache.lucene.geo.GeoEncodingUtils;
 import org.apache.lucene.index.LeafReaderContext;
 import org.elasticsearch.common.geo.GeoPoint;
+import org.elasticsearch.common.geo.GeoUtils;
+import org.elasticsearch.common.xcontent.support.XContentMapValues;
 import org.elasticsearch.search.lookup.SearchLookup;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 
@@ -27,6 +31,21 @@ import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
 public abstract class GeoPointFieldScript extends AbstractLongFieldScript {
     public static final ScriptContext<Factory> CONTEXT = newContext("geo_point_field", Factory.class);
 
+    public static final GeoPointFieldScript.Factory PARSE_FROM_SOURCE
+        = (field, params, lookup) -> (GeoPointFieldScript.LeafFactory) ctx -> new GeoPointFieldScript
+        (
+            field,
+            params,
+            lookup,
+            ctx
+        ) {
+
+        @Override
+        public void execute() {
+            emitFromSource();
+        }
+    };
+
     @SuppressWarnings("unused")
     public static final String[] PARAMETERS = {};
 
@@ -38,6 +57,8 @@ public abstract class GeoPointFieldScript extends AbstractLongFieldScript {
         GeoPointFieldScript newInstance(LeafReaderContext ctx);
     }
 
+    private final GeoPoint scratch = new GeoPoint();
+
     public GeoPointFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
         super(fieldName, params, searchLookup, ctx);
     }
@@ -56,7 +77,51 @@ public abstract class GeoPointFieldScript extends AbstractLongFieldScript {
         }
     }
 
-    protected void emit(double lat, double lon) {
+    @Override
+    protected List<Object> extractFromSource(String path) {
+        Object value = XContentMapValues.extractValue(path, leafSearchLookup.source().source());
+        if (value instanceof List<?>) {
+            @SuppressWarnings("unchecked")
+            List<Object> list = (List<Object>) value;
+            if (list.size() > 0 && list.get(0) instanceof Number) {
+                //[2, 1]: two values but one single point, return it as a list or each value will be seen as a different geopoint.
+                return Collections.singletonList(list);
+            }
+            //e.g. [ [2,1], {lat:2, lon:1} ]
+            return list;
+        }
+        //e.g. {lat: 2, lon: 1}
+        return Collections.singletonList(value);
+    }
+
+    @Override
+    protected void emitFromObject(Object value) {
+        if (value instanceof List<?>) {
+            List<?> values = (List<?>) value;
+            if (values.size() > 0 && values.get(0) instanceof Number) {
+                emitPoint(value);
+            } else {
+                for (Object point : values) {
+                    emitPoint(point);
+                }
+            }
+        } else {
+            emitPoint(value);
+        }
+    }
+
+    private void emitPoint(Object point) {
+        if (point != null) {
+            try {
+                GeoUtils.parseGeoPoint(point, scratch, true);
+            } catch(Exception e) {
+                //ignore
+            }
+            emit(scratch.lat(), scratch.lon());
+        }
+    }
+
+    protected final void emit(double lat, double lon) {
         int latitudeEncoded = encodeLatitude(lat);
         int longitudeEncoded = encodeLongitude(lon);
         emit(Long.valueOf((((long) latitudeEncoded) << 32) | (longitudeEncoded & 0xFFFFFFFFL)));

+ 25 - 0
server/src/main/java/org/elasticsearch/script/IpFieldScript.java

@@ -39,6 +39,20 @@ import java.util.function.Consumer;
 public abstract class IpFieldScript extends AbstractFieldScript {
     public static final ScriptContext<Factory> CONTEXT = newContext("ip_field", Factory.class);
 
+    public static final IpFieldScript.Factory PARSE_FROM_SOURCE
+        = (field, params, lookup) -> (IpFieldScript.LeafFactory) ctx -> new IpFieldScript
+        (
+            field,
+            params,
+            lookup,
+            ctx
+        ) {
+        @Override
+        public void execute() {
+            emitFromSource();
+        }
+    };
+
     @SuppressWarnings("unused")
     public static final String[] PARAMETERS = {};
 
@@ -93,6 +107,17 @@ public abstract class IpFieldScript extends AbstractFieldScript {
         return count;
     }
 
+    @Override
+    protected void emitFromObject(Object v) {
+        if (v instanceof String) {
+            try {
+                emit((String) v);
+            } catch (Exception e) {
+                // ignore parsing exceptions
+            }
+        }
+    }
+
     public final void emit(String v) {
         checkMaxSize(count);
         if (values.length < count + 1) {

+ 24 - 0
server/src/main/java/org/elasticsearch/script/LongFieldScript.java

@@ -9,6 +9,7 @@
 package org.elasticsearch.script;
 
 import org.apache.lucene.index.LeafReaderContext;
+import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.search.lookup.SearchLookup;
 
 import java.util.Map;
@@ -16,6 +17,20 @@ import java.util.Map;
 public abstract class LongFieldScript extends AbstractLongFieldScript {
     public static final ScriptContext<Factory> CONTEXT = newContext("long_field", Factory.class);
 
+    public static final LongFieldScript.Factory PARSE_FROM_SOURCE
+        = (field, params, lookup) -> (LongFieldScript.LeafFactory) ctx -> new LongFieldScript
+        (
+            field,
+            params,
+            lookup,
+            ctx
+        ) {
+        @Override
+        public void execute() {
+            emitFromSource();
+        }
+    };
+
     @SuppressWarnings("unused")
     public static final String[] PARAMETERS = {};
 
@@ -31,6 +46,15 @@ public abstract class LongFieldScript extends AbstractLongFieldScript {
         super(fieldName, params, searchLookup, ctx);
     }
 
+    @Override
+    protected void emitFromObject(Object v) {
+        try {
+            emit(NumberFieldMapper.NumberType.objectToLong(v, true));
+        } catch (Exception e) {
+            // ignore;
+        }
+    }
+
     public static class Emit {
         private final LongFieldScript script;
 

+ 21 - 0
server/src/main/java/org/elasticsearch/script/StringFieldScript.java

@@ -25,6 +25,20 @@ public abstract class StringFieldScript extends AbstractFieldScript {
 
     public static final ScriptContext<Factory> CONTEXT = newContext("keyword_field", Factory.class);
 
+    public static final StringFieldScript.Factory PARSE_FROM_SOURCE
+        = (field, params, lookup) -> (StringFieldScript.LeafFactory) ctx -> new StringFieldScript
+        (
+            field,
+            params,
+            lookup,
+            ctx
+        ) {
+        @Override
+        public void execute() {
+            emitFromSource();
+        }
+    };
+
     @SuppressWarnings("unused")
     public static final String[] PARAMETERS = {};
 
@@ -61,6 +75,13 @@ public abstract class StringFieldScript extends AbstractFieldScript {
         resultsForDoc(docId).forEach(consumer);
     }
 
+    @Override
+    protected void emitFromObject(Object v) {
+        if (v != null) {
+            emit(v.toString());
+        }
+    }
+
     public final void emit(String v) {
         checkMaxSize(results.size());
         chars += v.length();

+ 2 - 2
server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java

@@ -258,10 +258,10 @@ public abstract class NumberFieldMapperTests extends MapperTestCase {
     @SuppressWarnings("unchecked")
     protected <T> T compileScript(Script script, ScriptContext<T> context) {
         if (context == LongFieldScript.CONTEXT) {
-            return (T) LongScriptFieldType.PARSE_FROM_SOURCE;
+            return (T) LongFieldScript.PARSE_FROM_SOURCE;
         }
         if (context == DoubleFieldScript.CONTEXT) {
-            return (T) DoubleScriptFieldType.PARSE_FROM_SOURCE;
+            return (T) DoubleFieldScript.PARSE_FROM_SOURCE;
         }
         throw new UnsupportedOperationException("Unknown script " + script.getIdOrCode());
     }