Browse Source

Scripting: Remove SearchScript (#34730)

This commit removes the last non context based script class.
Ryan Ernst 7 năm trước cách đây
mục cha
commit
687dc1eb11
19 tập tin đã thay đổi với 100 bổ sung566 xóa
  1. 13 39
      modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScoreScript.java
  2. 37 79
      modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java
  3. 0 39
      modules/lang-painless/src/main/java/org/elasticsearch/painless/GenericElasticsearchScript.java
  4. 16 54
      modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java
  5. 0 112
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java
  6. 2 1
      modules/lang-painless/src/test/java/org/elasticsearch/painless/Debugger.java
  7. 1 1
      modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java
  8. 2 2
      modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java
  9. 3 3
      server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java
  10. 2 2
      server/src/main/java/org/elasticsearch/script/ExplainableScoreScript.java
  11. 1 1
      server/src/main/java/org/elasticsearch/script/FieldScript.java
  12. 2 1
      server/src/main/java/org/elasticsearch/script/ScoreScript.java
  13. 0 142
      server/src/main/java/org/elasticsearch/script/SearchScript.java
  14. 2 2
      server/src/test/java/org/elasticsearch/search/functionscore/ExplainableScriptIT.java
  15. 14 76
      test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java
  16. 3 4
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/monitoring/test/MockPainlessScriptEngine.java
  17. 1 6
      x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java
  18. 1 1
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/WatcherMockScriptPlugin.java
  19. 0 1
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/SearchInputTests.java

+ 13 - 39
modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionSearchScript.java → modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScoreScript.java

@@ -26,27 +26,23 @@ import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.search.DoubleValues;
 import org.apache.lucene.search.DoubleValuesSource;
 import org.elasticsearch.script.GeneralScriptException;
-import org.elasticsearch.script.SearchScript;
+import org.elasticsearch.script.ScoreScript;
 
 import java.io.IOException;
 
 /**
  * A bridge to evaluate an {@link Expression} against {@link Bindings} in the context
- * of a {@link SearchScript}.
+ * of a {@link ScoreScript}.
  */
-class ExpressionSearchScript implements SearchScript.LeafFactory {
+class ExpressionScoreScript implements ScoreScript.LeafFactory {
 
-    final Expression exprScript;
-    final SimpleBindings bindings;
-    final DoubleValuesSource source;
-    final ReplaceableConstDoubleValueSource specialValue; // _value
-    final boolean needsScores;
+    private final Expression exprScript;
+    private final DoubleValuesSource source;
+    private final boolean needsScores;
 
-    ExpressionSearchScript(Expression e, SimpleBindings b, ReplaceableConstDoubleValueSource v, boolean needsScores) {
-        exprScript = e;
-        bindings = b;
-        source = exprScript.getDoubleValuesSource(bindings);
-        specialValue = v;
+    ExpressionScoreScript(Expression e, SimpleBindings b, boolean needsScores) {
+        this.exprScript = e;
+        this.source = exprScript.getDoubleValuesSource(b);
         this.needsScores = needsScores;
     }
 
@@ -55,15 +51,14 @@ class ExpressionSearchScript implements SearchScript.LeafFactory {
         return needsScores;
     }
 
-
     @Override
-    public SearchScript newInstance(final LeafReaderContext leaf) throws IOException {
-        return new SearchScript(null, null, null) {
+    public ScoreScript newInstance(final LeafReaderContext leaf) throws IOException {
+        return new ScoreScript(null, null, null) {
             // Fake the scorer until setScorer is called.
             DoubleValues values = source.getValues(leaf, new DoubleValues() {
                 @Override
                 public double doubleValue() throws IOException {
-                    return getScore();
+                    return get_score();
                 }
 
                 @Override
@@ -73,10 +68,7 @@ class ExpressionSearchScript implements SearchScript.LeafFactory {
             });
 
             @Override
-            public Object run() { return Double.valueOf(runAsDouble()); }
-
-            @Override
-            public double runAsDouble() {
+            public double execute() {
                 try {
                     return values.doubleValue();
                 } catch (Exception exception) {
@@ -92,24 +84,6 @@ class ExpressionSearchScript implements SearchScript.LeafFactory {
                     throw new IllegalStateException("Can't advance to doc using " + exprScript, e);
                 }
             }
-
-            @Override
-            public void setNextAggregationValue(Object value) {
-                // _value isn't used in script if specialValue == null
-                if (specialValue != null) {
-                    if (value instanceof Number) {
-                        specialValue.setValue(((Number)value).doubleValue());
-                    } else {
-                        throw new GeneralScriptException("Cannot use expression with text variable using " + exprScript);
-                    }
-                }
-            }
-
-            @Override
-            public void setNextVar(String name, Object value) {
-                // other per-document variables aren't supported yet, even if they are numbers
-                // but we shouldn't encourage this anyway.
-            }
         };
     }
 

+ 37 - 79
modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngine.java

@@ -23,10 +23,8 @@ import org.apache.lucene.expressions.Expression;
 import org.apache.lucene.expressions.SimpleBindings;
 import org.apache.lucene.expressions.js.JavascriptCompiler;
 import org.apache.lucene.expressions.js.VariableContext;
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.DoubleConstValueSource;
-import org.apache.lucene.search.Scorable;
 import org.apache.lucene.search.SortField;
 import org.elasticsearch.SpecialPermission;
 import org.elasticsearch.common.Nullable;
@@ -48,11 +46,9 @@ import org.elasticsearch.script.ScoreScript;
 import org.elasticsearch.script.ScriptContext;
 import org.elasticsearch.script.ScriptEngine;
 import org.elasticsearch.script.ScriptException;
-import org.elasticsearch.script.SearchScript;
 import org.elasticsearch.script.TermsSetQueryScript;
 import org.elasticsearch.search.lookup.SearchLookup;
 
-import java.io.IOException;
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -63,8 +59,9 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * Provides the infrastructure for Lucene expressions as a scripting language for Elasticsearch.  Only
- * {@link SearchScript}s are supported.
+ * Provides the infrastructure for Lucene expressions as a scripting language for Elasticsearch.
+ *
+ * Only contexts returning numeric types or {@link Object} are supported.
  */
 public class ExpressionScriptEngine extends AbstractComponent implements ScriptEngine {
 
@@ -111,10 +108,7 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
                 }
             }
         });
-        if (context.instanceClazz.equals(SearchScript.class)) {
-            SearchScript.Factory factory = (p, lookup) -> newSearchScript(expr, lookup, p);
-            return context.factoryClazz.cast(factory);
-        } else if (context.instanceClazz.equals(BucketAggregationScript.class)) {
+        if (context.instanceClazz.equals(BucketAggregationScript.class)) {
             return context.factoryClazz.cast(newBucketAggregationScriptFactory(expr));
         } else if (context.instanceClazz.equals(BucketAggregationSelectorScript.class)) {
             BucketAggregationScript.Factory factory = newBucketAggregationScriptFactory(expr);
@@ -178,40 +172,6 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
         };
     }
 
-    private SearchScript.LeafFactory newSearchScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
-        // NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings,
-        // instead of complicating SimpleBindings (which should stay simple)
-        SimpleBindings bindings = new SimpleBindings();
-        ReplaceableConstDoubleValueSource specialValue = null;
-        boolean needsScores = false;
-        for (String variable : expr.variables) {
-            try {
-                if (variable.equals("_score")) {
-                    bindings.add(new SortField("_score", SortField.Type.SCORE));
-                    needsScores = true;
-                } else if (variable.equals("_value")) {
-                    specialValue = new ReplaceableConstDoubleValueSource();
-                    bindings.add("_value", specialValue);
-                    // noop: _value is special for aggregations, and is handled in ExpressionScriptBindings
-                    // TODO: if some uses it in a scoring expression, they will get a nasty failure when evaluating...need a
-                    // way to know this is for aggregations and so _value is ok to have...
-                } else if (vars != null && vars.containsKey(variable)) {
-                    bindFromParams(vars, bindings, variable);
-                } else {
-                    // delegate valuesource creation based on field's type
-                    // there are three types of "fields" to expressions, and each one has a different "api" of variables and methods.
-                    final ValueSource valueSource = getDocValueSource(variable, lookup);
-                    needsScores |= valueSource.getSortField(false).needsScores();
-                    bindings.add(variable, valueSource.asDoubleValuesSource());
-                }
-            } catch (Exception e) {
-                // we defer "binding" of variables until here: give context for that variable
-                throw convertToScriptException("link error", expr.sourceText, variable, e);
-            }
-        }
-        return new ExpressionSearchScript(expr, bindings, specialValue, needsScores);
-    }
-
     private NumberSortScript.LeafFactory newSortScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
         // NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings,
         // instead of complicating SimpleBindings (which should stay simple)
@@ -315,13 +275,13 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
      * See https://github.com/elastic/elasticsearch/issues/26429.
      */
     private FilterScript.LeafFactory newFilterScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
-        SearchScript.LeafFactory searchLeafFactory = newSearchScript(expr, lookup, vars);
+        ScoreScript.LeafFactory searchLeafFactory = newScoreScript(expr, lookup, vars);
         return ctx -> {
-            SearchScript script = searchLeafFactory.newInstance(ctx);
+            ScoreScript script = searchLeafFactory.newInstance(ctx);
             return new FilterScript(vars, lookup, ctx) {
                 @Override
                 public boolean execute() {
-                    return script.runAsDouble() != 0.0;
+                    return script.execute() != 0.0;
                 }
                 @Override
                 public void setDocument(int docid) {
@@ -332,39 +292,37 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
     }
 
     private ScoreScript.LeafFactory newScoreScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
-        SearchScript.LeafFactory searchLeafFactory = newSearchScript(expr, lookup, vars);
-        return new ScoreScript.LeafFactory() {
-            @Override
-            public boolean needs_score() {
-                return searchLeafFactory.needs_score();
-            }
-
-            @Override
-            public ScoreScript newInstance(LeafReaderContext ctx) throws IOException {
-                SearchScript script = searchLeafFactory.newInstance(ctx);
-                return new ScoreScript(vars, lookup, ctx) {
-                    @Override
-                    public double execute() {
-                        return script.runAsDouble();
-                    }
-
-                    @Override
-                    public void setDocument(int docid) {
-                        script.setDocument(docid);
-                    }
-
-                    @Override
-                    public void setScorer(Scorable scorer) {
-                        script.setScorer(scorer);
-                    }
-
-                    @Override
-                    public double get_score() {
-                        return script.getScore();
-                    }
-                };
+        // NOTE: if we need to do anything complicated with bindings in the future, we can just extend Bindings,
+        // instead of complicating SimpleBindings (which should stay simple)
+        SimpleBindings bindings = new SimpleBindings();
+        ReplaceableConstDoubleValueSource specialValue = null;
+        boolean needsScores = false;
+        for (String variable : expr.variables) {
+            try {
+                if (variable.equals("_score")) {
+                    bindings.add(new SortField("_score", SortField.Type.SCORE));
+                    needsScores = true;
+                } else if (variable.equals("_value")) {
+                    specialValue = new ReplaceableConstDoubleValueSource();
+                    bindings.add("_value", specialValue);
+                    // noop: _value is special for aggregations, and is handled in ExpressionScriptBindings
+                    // TODO: if some uses it in a scoring expression, they will get a nasty failure when evaluating...need a
+                    // way to know this is for aggregations and so _value is ok to have...
+                } else if (vars != null && vars.containsKey(variable)) {
+                    bindFromParams(vars, bindings, variable);
+                } else {
+                    // delegate valuesource creation based on field's type
+                    // there are three types of "fields" to expressions, and each one has a different "api" of variables and methods.
+                    final ValueSource valueSource = getDocValueSource(variable, lookup);
+                    needsScores |= valueSource.getSortField(false).needsScores();
+                    bindings.add(variable, valueSource.asDoubleValuesSource());
+                }
+            } catch (Exception e) {
+                // we defer "binding" of variables until here: give context for that variable
+                throw convertToScriptException("link error", expr.sourceText, variable, e);
             }
-        };
+        }
+        return new ExpressionScoreScript(expr, bindings, needsScores);
     }
 
     /**

+ 0 - 39
modules/lang-painless/src/main/java/org/elasticsearch/painless/GenericElasticsearchScript.java

@@ -1,39 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.elasticsearch.painless;
-
-import org.elasticsearch.index.fielddata.ScriptDocValues;
-
-import java.util.Map;
-
-/**
- * Generic script interface that Painless implements for all Elasticsearch scripts.
- */
-public abstract class GenericElasticsearchScript {
-
-    public GenericElasticsearchScript() {}
-
-    public static final String[] PARAMETERS = new String[] {"params", "_score", "doc", "_value", "ctx"};
-    public abstract Object execute(
-        Map<String, Object> params, double _score, Map<String, ScriptDocValues<?>> doc, Object _value, Map<?, ?> ctx);
-
-    public abstract boolean needs_score();
-    public abstract boolean needsCtx();
-}

+ 16 - 54
modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.painless;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.elasticsearch.SpecialPermission;
 import org.elasticsearch.common.component.AbstractComponent;
 import org.elasticsearch.common.settings.Settings;
@@ -29,7 +28,6 @@ import org.elasticsearch.painless.spi.Whitelist;
 import org.elasticsearch.script.ScriptContext;
 import org.elasticsearch.script.ScriptEngine;
 import org.elasticsearch.script.ScriptException;
-import org.elasticsearch.script.SearchScript;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
@@ -37,7 +35,6 @@ import org.objectweb.asm.commons.GeneratorAdapter;
 
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.security.AccessControlContext;
 import java.security.AccessController;
@@ -101,13 +98,8 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
 
         for (Map.Entry<ScriptContext<?>, List<Whitelist>> entry : contexts.entrySet()) {
             ScriptContext<?> context = entry.getKey();
-            if (context.instanceClazz.equals(SearchScript.class)) {
-                contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, null, null,
-                        PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
-            } else {
-                contextsToCompilers.put(context, new Compiler(context.instanceClazz, context.factoryClazz, context.statefulFactoryClazz,
-                        PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
-            }
+            contextsToCompilers.put(context, new Compiler(context.instanceClazz, context.factoryClazz, context.statefulFactoryClazz,
+                    PainlessLookupBuilder.buildFromWhitelists(entry.getValue())));
         }
 
         this.contextsToCompilers = Collections.unmodifiableMap(contextsToCompilers);
@@ -126,54 +118,24 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
     public <T> T compile(String scriptName, String scriptSource, ScriptContext<T> context, Map<String, String> params) {
         Compiler compiler = contextsToCompilers.get(context);
 
-        if (context.instanceClazz.equals(SearchScript.class)) {
-            Constructor<?> constructor = compile(compiler, scriptName, scriptSource, params);
-            boolean needsScore;
+        // Check we ourselves are not being called by unprivileged code.
+        SpecialPermission.check();
 
-            try {
-                GenericElasticsearchScript newInstance = (GenericElasticsearchScript)constructor.newInstance();
-                needsScore = newInstance.needs_score();
-            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
-                throw new IllegalArgumentException("internal error");
+        // Create our loader (which loads compiled code with no permissions).
+        final Loader loader = AccessController.doPrivileged(new PrivilegedAction<Loader>() {
+            @Override
+            public Loader run() {
+                return compiler.createLoader(getClass().getClassLoader());
             }
+        });
 
-            SearchScript.Factory factory = (p, lookup) -> new SearchScript.LeafFactory() {
-                @Override
-                public SearchScript newInstance(final LeafReaderContext context) {
-                    try {
-                        // a new instance is required for the class bindings model to work correctly
-                        GenericElasticsearchScript newInstance = (GenericElasticsearchScript)constructor.newInstance();
-                        return new ScriptImpl(newInstance, p, lookup, context);
-                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
-                        throw new IllegalArgumentException("internal error");
-                    }
-                }
-                @Override
-                public boolean needs_score() {
-                    return needsScore;
-                }
-            };
-            return context.factoryClazz.cast(factory);
-        } else {
-            // Check we ourselves are not being called by unprivileged code.
-            SpecialPermission.check();
-
-            // Create our loader (which loads compiled code with no permissions).
-            final Loader loader = AccessController.doPrivileged(new PrivilegedAction<Loader>() {
-                @Override
-                public Loader run() {
-                    return compiler.createLoader(getClass().getClassLoader());
-                }
-            });
-
-            MainMethodReserved reserved = new MainMethodReserved();
-            compile(contextsToCompilers.get(context), loader, reserved, scriptName, scriptSource, params);
+        MainMethodReserved reserved = new MainMethodReserved();
+        compile(contextsToCompilers.get(context), loader, reserved, scriptName, scriptSource, params);
 
-            if (context.statefulFactoryClazz != null) {
-                return generateFactory(loader, context, reserved, generateStatefulFactory(loader, context, reserved));
-            } else {
-                return generateFactory(loader, context, reserved, WriterConstants.CLASS_TYPE);
-            }
+        if (context.statefulFactoryClazz != null) {
+            return generateFactory(loader, context, reserved, generateStatefulFactory(loader, context, reserved));
+        } else {
+            return generateFactory(loader, context, reserved, WriterConstants.CLASS_TYPE);
         }
     }
 

+ 0 - 112
modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptImpl.java

@@ -1,112 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.elasticsearch.painless;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.elasticsearch.script.SearchScript;
-import org.elasticsearch.search.lookup.LeafSearchLookup;
-import org.elasticsearch.search.lookup.SearchLookup;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.DoubleSupplier;
-import java.util.function.Function;
-
-/**
- * ScriptImpl can be used as a {@link SearchScript}
- * to run a previously compiled Painless script.
- */
-final class ScriptImpl extends SearchScript {
-
-    /**
-     * The Painless script that can be run.
-     */
-    private final GenericElasticsearchScript script;
-
-    /**
-     * A map that can be used to access input parameters at run-time.
-     */
-    private final Map<String, Object> variables;
-
-    /**
-     * Looks up the {@code _score} from {@link #scorer} if {@code _score} is used, otherwise returns {@code 0.0}.
-     */
-    private final DoubleSupplier scoreLookup;
-
-    /**
-     * Looks up the {@code ctx} from the {@link #variables} if {@code ctx} is used, otherwise return {@code null}.
-     */
-    private final Function<Map<String, Object>, Map<?, ?>> ctxLookup;
-
-    /**
-     * Current _value for aggregation
-     * @see #setNextAggregationValue(Object)
-     */
-    private Object aggregationValue;
-
-    /**
-     * Creates a ScriptImpl for the a previously compiled Painless script.
-     * @param script The previously compiled Painless script.
-     * @param vars The initial variables to run the script with.
-     * @param lookup The lookup to allow search fields to be available if this is run as a search script.
-     */
-    ScriptImpl(GenericElasticsearchScript script, Map<String, Object> vars, SearchLookup lookup, LeafReaderContext leafContext) {
-        super(null, lookup, leafContext);
-        this.script = script;
-        this.variables = new HashMap<>();
-
-        if (vars != null) {
-            variables.putAll(vars);
-        }
-        LeafSearchLookup leafLookup = getLeafLookup();
-        if (leafLookup != null) {
-            variables.putAll(leafLookup.asMap());
-        }
-
-        scoreLookup = script.needs_score() ? this::getScore : () -> 0.0;
-        ctxLookup = script.needsCtx() ? variables -> (Map<?, ?>) variables.get("ctx") : variables -> null;
-    }
-
-    @Override
-    public Map<String, Object> getParams() {
-        return variables;
-    }
-
-    @Override
-    public void setNextVar(final String name, final Object value) {
-        variables.put(name, value);
-    }
-
-    @Override
-    public void setNextAggregationValue(Object value) {
-        this.aggregationValue = value;
-    }
-
-    @Override
-    public Object run() {
-        return script.execute(variables, scoreLookup.getAsDouble(), getDoc(), aggregationValue, ctxLookup.apply(variables));
-    }
-
-    @Override
-    public double runAsDouble() {
-        return ((Number)run()).doubleValue();
-    }
-
-}

+ 2 - 1
modules/lang-painless/src/test/java/org/elasticsearch/painless/Debugger.java

@@ -19,6 +19,7 @@
 
 package org.elasticsearch.painless;
 
+import org.elasticsearch.painless.PainlessExecuteAction.PainlessTestScript;
 import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
 import org.elasticsearch.painless.spi.Whitelist;
 import org.objectweb.asm.util.Textifier;
@@ -31,7 +32,7 @@ final class Debugger {
 
     /** compiles source to bytecode, and returns debugging output */
     static String toString(final String source) {
-        return toString(GenericElasticsearchScript.class, source, new CompilerSettings());
+        return toString(PainlessTestScript.class, source, new CompilerSettings());
     }
 
     /** compiles to bytecode, and returns debugging output */

+ 1 - 1
modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java

@@ -91,7 +91,7 @@ public abstract class ScriptTestCase extends ESTestCase {
     public Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, boolean picky) {
         // test for ambiguity errors before running the actual script if picky is true
         if (picky) {
-            ScriptClassInfo scriptClassInfo = new ScriptClassInfo(PAINLESS_LOOKUP, GenericElasticsearchScript.class);
+            ScriptClassInfo scriptClassInfo = new ScriptClassInfo(PAINLESS_LOOKUP, PainlessTestScript.class);
             CompilerSettings pickySettings = new CompilerSettings();
             pickySettings.setPicky(true);
             pickySettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(scriptEngineSettings()));

+ 2 - 2
modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java

@@ -21,10 +21,10 @@ package org.elasticsearch.painless.node;
 
 import org.elasticsearch.painless.CompilerSettings;
 import org.elasticsearch.painless.FeatureTest;
-import org.elasticsearch.painless.GenericElasticsearchScript;
 import org.elasticsearch.painless.Locals.Variable;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.Operation;
+import org.elasticsearch.painless.PainlessExecuteAction.PainlessTestScript;
 import org.elasticsearch.painless.ScriptClassInfo;
 import org.elasticsearch.painless.antlr.Walker;
 import org.elasticsearch.painless.lookup.PainlessCast;
@@ -897,7 +897,7 @@ public class NodeToStringTests extends ESTestCase {
     }
 
     private SSource walk(String code) {
-        ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, GenericElasticsearchScript.class);
+        ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, PainlessTestScript.class);
         CompilerSettings compilerSettings = new CompilerSettings();
         compilerSettings.setRegexesEnabled(true);
         try {

+ 3 - 3
server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java

@@ -22,7 +22,7 @@ package org.elasticsearch.common.lucene.search.function;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.Scorable;
-import org.elasticsearch.script.ExplainableSearchScript;
+import org.elasticsearch.script.ExplainableScoreScript;
 import org.elasticsearch.script.ScoreScript;
 import org.elasticsearch.script.Script;
 
@@ -75,11 +75,11 @@ public class ScriptScoreFunction extends ScoreFunction {
             @Override
             public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException {
                 Explanation exp;
-                if (leafScript instanceof ExplainableSearchScript) {
+                if (leafScript instanceof ExplainableScoreScript) {
                     leafScript.setDocument(docId);
                     scorer.docid = docId;
                     scorer.score = subQueryScore.getValue().floatValue();
-                    exp = ((ExplainableSearchScript) leafScript).explain(subQueryScore);
+                    exp = ((ExplainableScoreScript) leafScript).explain(subQueryScore);
                 } else {
                     double score = score(docId, subQueryScore.getValue().floatValue());
                     String explanation = "script score function, computed with script:\"" + sScript + "\"";

+ 2 - 2
server/src/main/java/org/elasticsearch/script/ExplainableSearchScript.java → server/src/main/java/org/elasticsearch/script/ExplainableScoreScript.java

@@ -43,11 +43,11 @@ import org.apache.lucene.search.Explanation;
 import java.io.IOException;
 
 /**
- * To be implemented by {@link SearchScript} which can provided an {@link Explanation} of the score
+ * To be implemented by {@link ScoreScript} which can provided an {@link Explanation} of the score
  * This is currently not used inside elasticsearch but it is used, see for example here:
  * https://github.com/elastic/elasticsearch/issues/8561
  */
-public interface ExplainableSearchScript {
+public interface ExplainableScoreScript {
 
     /**
      * Build the explanation of the current document being scored

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

@@ -94,7 +94,7 @@ public abstract class FieldScript {
         leafLookup.setDocument(docid);
     }
 
-    /** A factory to construct {@link SearchScript} instances. */
+    /** A factory to construct {@link FieldScript} instances. */
     public interface LeafFactory {
         FieldScript newInstance(LeafReaderContext ctx) throws IOException;
     }

+ 2 - 1
server/src/main/java/org/elasticsearch/script/ScoreScript.java

@@ -46,7 +46,8 @@ public abstract class ScoreScript {
 
     public ScoreScript(Map<String, Object> params, SearchLookup lookup, LeafReaderContext leafContext) {
         this.params = params;
-        this.leafLookup = lookup.getLeafSearchLookup(leafContext);
+        // null check needed b/c of expression engine subclass
+        this.leafLookup = lookup == null ? null : lookup.getLeafSearchLookup(leafContext);
     }
 
     public abstract double execute();

+ 0 - 142
server/src/main/java/org/elasticsearch/script/SearchScript.java

@@ -1,142 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.elasticsearch.script;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.Scorable;
-import org.elasticsearch.ElasticsearchException;
-import org.elasticsearch.common.lucene.ScorerAware;
-import org.elasticsearch.search.lookup.LeafDocLookup;
-import org.elasticsearch.search.lookup.LeafSearchLookup;
-import org.elasticsearch.search.lookup.SearchLookup;
-
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * A generic script used for per document use cases.
- *
- * Using a {@link SearchScript} works as follows:
- * <ol>
- *     <li>Construct a {@link Factory} using {@link ScriptService#compile(Script, ScriptContext)}</li>
- *     <li>Construct a {@link LeafFactory} for a an index using {@link Factory#newFactory(Map, SearchLookup)}</li>
- *     <li>Construct a {@link SearchScript} for a Lucene segment using {@link LeafFactory#newInstance(LeafReaderContext)}</li>
- *     <li>Call {@link #setDocument(int)} to indicate which document in the segment the script should be run for next</li>
- *     <li>Call one of the {@code run} methods: {@link #run()} or {@link #runAsDouble()}</li>
- * </ol>
- */
-public abstract class SearchScript implements ScorerAware {
-
-    /** The generic runtime parameters for the script. */
-    private final Map<String, Object> params;
-
-    /** A leaf lookup for the bound segment this script will operate on. */
-    private final LeafSearchLookup leafLookup;
-
-    /** A scorer that will return the score for the current document when the script is run. */
-    private Scorable scorer;
-
-    public SearchScript(Map<String, Object> params, SearchLookup lookup, LeafReaderContext leafContext) {
-        this.params = params;
-        // TODO: remove leniency when painless does not implement SearchScript for executable script cases
-        this.leafLookup = leafContext == null ? null : lookup.getLeafSearchLookup(leafContext);
-    }
-
-    /** Return the parameters for this script. */
-    public Map<String, Object> getParams() {
-        return params;
-    }
-
-    /** The leaf lookup for the Lucene segment this script was created for. */
-    protected final LeafSearchLookup getLeafLookup() {
-        return leafLookup;
-    }
-
-    /** The doc lookup for the Lucene segment this script was created for. */
-    public final LeafDocLookup getDoc() {
-        // TODO: remove leniency when painless does not implement SearchScript for executable script cases
-        return leafLookup == null ? null : leafLookup.doc();
-    }
-
-    /** Set the current document to run the script on next. */
-    public void setDocument(int docid) {
-        // TODO: remove leniency when painless does not implement SearchScript for executable script cases
-        if (leafLookup != null) {
-            leafLookup.setDocument(docid);
-        }
-    }
-
-    @Override
-    public void setScorer(Scorable scorer) {
-        this.scorer = scorer;
-    }
-
-    /** Return the score of the current document. */
-    public double getScore() {
-        // TODO: remove leniency when painless does not implement SearchScript for executable script cases
-        if (scorer == null) {
-            return 0.0d;
-        }
-        try {
-            return scorer.score();
-        } catch (IOException e) {
-            throw new ElasticsearchException("couldn't lookup score", e);
-        }
-    }
-
-    /**
-     * Sets per-document aggregation {@code _value}.
-     * <p>
-     * The default implementation just calls {@code setNextVar("_value", value)} but
-     * some engines might want to handle this differently for better performance.
-     * <p>
-     * @param value per-document value, typically a String, Long, or Double
-     */
-    public void setNextAggregationValue(Object value) {
-        setNextVar("_value", value);
-    }
-
-    public void setNextVar(String field, Object value) {}
-
-
-    public Object run() {
-        return runAsDouble();
-    }
-
-    /** Return the result as a double. This is the main use case of search script, used for document scoring. */
-    public abstract double runAsDouble();
-
-    /** A factory to construct {@link SearchScript} instances. */
-    public interface LeafFactory {
-        SearchScript newInstance(LeafReaderContext ctx) throws IOException;
-
-        /**
-         * Return {@code true} if the script needs {@code _score} calculated, or {@code false} otherwise.
-         */
-        boolean needs_score();
-    }
-
-    /** A factory to construct stateful {@link SearchScript} factories for a specific index. */
-    public interface Factory {
-        LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
-    }
-
-    /** The context used to compile {@link SearchScript} factories. */
-    public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("search", Factory.class);
-}

+ 2 - 2
server/src/test/java/org/elasticsearch/search/functionscore/ExplainableScriptIT.java

@@ -29,7 +29,7 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.plugins.ScriptPlugin;
-import org.elasticsearch.script.ExplainableSearchScript;
+import org.elasticsearch.script.ExplainableScoreScript;
 import org.elasticsearch.script.ScoreScript;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.script.ScriptContext;
@@ -94,7 +94,7 @@ public class ExplainableScriptIT extends ESIntegTestCase {
         }
     }
 
-    static class MyScript extends ScoreScript implements ExplainableSearchScript {
+    static class MyScript extends ScoreScript implements ExplainableScoreScript {
 
         MyScript(Map<String, Object> params, SearchLookup lookup, LeafReaderContext leafContext) {
             super(params, lookup, leafContext);

+ 14 - 76
test/framework/src/main/java/org/elasticsearch/script/MockScriptEngine.java

@@ -90,8 +90,16 @@ public class MockScriptEngine implements ScriptEngine {
                     "did you declare the mocked script?");
         }
         MockCompiledScript mockCompiled = new MockCompiledScript(name, params, source, script);
-        if (context.instanceClazz.equals(SearchScript.class)) {
-            SearchScript.Factory factory = mockCompiled::createSearchScript;
+        if (context.instanceClazz.equals(FieldScript.class)) {
+            FieldScript.Factory factory = (parameters, lookup) ->
+                ctx -> new FieldScript(parameters, lookup, ctx) {
+                    @Override
+                    public Object execute() {
+                        Map<String, Object> vars = createVars(parameters);
+                        vars.putAll(getLeafLookup().asMap());
+                        return script.apply(vars);
+                    }
+                };
             return context.factoryClazz.cast(factory);
         } else if (context.instanceClazz.equals(FieldScript.class)) {
             FieldScript.Factory factory = (parameters, lookup) ->
@@ -311,20 +319,6 @@ public class MockScriptEngine implements ScriptEngine {
             return name;
         }
 
-        public SearchScript.LeafFactory createSearchScript(Map<String, Object> params, SearchLookup lookup) {
-            Map<String, Object> context = new HashMap<>();
-            if (options != null) {
-                context.putAll(options); // TODO: remove this once scripts know to look for options under options key
-                context.put("options", options);
-            }
-            if (params != null) {
-                context.putAll(params); // TODO: remove this once scripts know to look for params under params key
-                context.put("params", params);
-            }
-            return new MockSearchScript(lookup, context, script != null ? script : ctx -> source);
-        }
-
-
         public FilterScript.LeafFactory createFilterScript(Map<String, Object> params, SearchLookup lookup) {
             return new MockFilterScript(lookup, params, script);
         }
@@ -361,62 +355,6 @@ public class MockScriptEngine implements ScriptEngine {
         }
     }
 
-    public class MockSearchScript implements SearchScript.LeafFactory {
-
-        private final Function<Map<String, Object>, Object> script;
-        private final Map<String, Object> vars;
-        private final SearchLookup lookup;
-
-        public MockSearchScript(SearchLookup lookup, Map<String, Object> vars, Function<Map<String, Object>, Object> script) {
-            this.lookup = lookup;
-            this.vars = vars;
-            this.script = script;
-        }
-
-        @Override
-        public SearchScript newInstance(LeafReaderContext context) throws IOException {
-            LeafSearchLookup leafLookup = lookup.getLeafSearchLookup(context);
-
-            Map<String, Object> ctx = new HashMap<>(leafLookup.asMap());
-            if (vars != null) {
-                ctx.putAll(vars);
-            }
-
-            return new SearchScript(vars, lookup, context) {
-                @Override
-                public Object run() {
-                    return script.apply(ctx);
-                }
-
-                @Override
-                public double runAsDouble() {
-                    return ((Number) run()).doubleValue();
-                }
-
-                @Override
-                public void setNextVar(String name, Object value) {
-                    ctx.put(name, value);
-                }
-
-                @Override
-                public void setScorer(Scorable scorer) {
-                    ctx.put("_score", new ScoreAccessor(scorer));
-                }
-
-                @Override
-                public void setDocument(int doc) {
-                    leafLookup.setDocument(doc);
-                }
-            };
-        }
-
-        @Override
-        public boolean needs_score() {
-            return true;
-        }
-    }
-
-
     public static class MockFilterScript implements FilterScript.LeafFactory {
 
         private final Function<Map<String, Object>, Object> script;
@@ -602,10 +540,10 @@ public class MockScriptEngine implements ScriptEngine {
 
     public class MockScoreScript implements ScoreScript.Factory {
 
-        private final Function<Map<String, Object>, Object> scripts;
+        private final Function<Map<String, Object>, Object> script;
 
-        MockScoreScript(Function<Map<String, Object>, Object> scripts) {
-            this.scripts = scripts;
+        public MockScoreScript(Function<Map<String, Object>, Object> script) {
+            this.script = script;
         }
 
         @Override
@@ -627,7 +565,7 @@ public class MockScriptEngine implements ScriptEngine {
                             if (scorerHolder[0] != null) {
                                 vars.put("_score", new ScoreAccessor(scorerHolder[0]));
                             }
-                            return ((Number) scripts.apply(vars)).doubleValue();
+                            return ((Number) script.apply(vars)).doubleValue();
                         }
 
                         @Override

+ 3 - 4
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/monitoring/test/MockPainlessScriptEngine.java

@@ -8,9 +8,9 @@ package org.elasticsearch.xpack.core.monitoring.test;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.script.MockScriptEngine;
 import org.elasticsearch.script.MockScriptPlugin;
+import org.elasticsearch.script.ScoreScript;
 import org.elasticsearch.script.ScriptContext;
 import org.elasticsearch.script.ScriptEngine;
-import org.elasticsearch.script.SearchScript;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -43,9 +43,8 @@ public class MockPainlessScriptEngine extends MockScriptEngine {
 
     @Override
     public <T> T compile(String name, String script, ScriptContext<T> context, Map<String, String> options) {
-        MockCompiledScript compiledScript = new MockCompiledScript(name, options, script, p -> script);
-        if (context.instanceClazz.equals(SearchScript.class)) {
-            return context.factoryClazz.cast((SearchScript.Factory) compiledScript::createSearchScript);
+        if (context.instanceClazz.equals(ScoreScript.class)) {
+            return context.factoryClazz.cast(new MockScoreScript(p -> 0.0));
         }
         throw new IllegalArgumentException("mock painless does not know how to handle context [" + context.name + "]");
     }

+ 1 - 6
x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java

@@ -50,7 +50,6 @@ import org.elasticsearch.rest.RestController;
 import org.elasticsearch.rest.RestHandler;
 import org.elasticsearch.script.ScriptContext;
 import org.elasticsearch.script.ScriptService;
-import org.elasticsearch.script.SearchScript;
 import org.elasticsearch.script.TemplateScript;
 import org.elasticsearch.threadpool.ExecutorBuilder;
 import org.elasticsearch.threadpool.FixedExecutorBuilder;
@@ -223,9 +222,6 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin, Reloa
         Setting.byteSizeSetting("xpack.watcher.bulk.size", new ByteSizeValue(1, ByteSizeUnit.MB),
             new ByteSizeValue(1, ByteSizeUnit.MB), new ByteSizeValue(10, ByteSizeUnit.MB), NodeScope);
 
-
-    public static final ScriptContext<SearchScript.Factory> SCRIPT_SEARCH_CONTEXT =
-        new ScriptContext<>("xpack", SearchScript.Factory.class);
     public static final ScriptContext<TemplateScript.Factory> SCRIPT_TEMPLATE_CONTEXT
         = new ScriptContext<>("xpack_template", TemplateScript.Factory.class);
 
@@ -670,8 +666,7 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin, Reloa
 
     @Override
     public List<ScriptContext<?>> getContexts() {
-        return Arrays.asList(Watcher.SCRIPT_SEARCH_CONTEXT, WatcherTransformScript.CONTEXT,
-            WatcherConditionScript.CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT);
+        return Arrays.asList(WatcherTransformScript.CONTEXT, WatcherConditionScript.CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT);
     }
 
     @Override

+ 1 - 1
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/WatcherMockScriptPlugin.java

@@ -56,7 +56,7 @@ public abstract class WatcherMockScriptPlugin extends MockScriptPlugin {
     }
 
     public static final List<ScriptContext<?>> CONTEXTS = Collections.unmodifiableList(Arrays.asList(
-        WatcherConditionScript.CONTEXT, WatcherTransformScript.CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT, Watcher.SCRIPT_SEARCH_CONTEXT
+        WatcherConditionScript.CONTEXT, WatcherTransformScript.CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT
     ));
 
     @Override

+ 0 - 1
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/SearchInputTests.java

@@ -76,7 +76,6 @@ public class SearchInputTests extends ESTestCase {
         engines.put(MockMustacheScriptEngine.NAME, new MockMustacheScriptEngine());
         Map<String, ScriptContext<?>> contexts = new HashMap<>();
         contexts.put(Watcher.SCRIPT_TEMPLATE_CONTEXT.name, Watcher.SCRIPT_TEMPLATE_CONTEXT);
-        contexts.put(Watcher.SCRIPT_SEARCH_CONTEXT.name, Watcher.SCRIPT_SEARCH_CONTEXT);
         contexts.put(WatcherTransformScript.CONTEXT.name, WatcherTransformScript.CONTEXT);
         scriptService = new ScriptService(Settings.EMPTY, engines, contexts);