Scripting: Groundwork for caching script results (#49895) (#49944)

In order to cache script results in the query shard cache, we need to
check if scripts are deterministic.  This change adds a default method
to the script factories, `isResultDeterministic() -> false` which is
used by the `QueryShardContext`.

Script results were never cached and that does not change here.  Future
changes will implement this method based on whether the results of the
scripts are deterministic or not and therefore cacheable.

Refs: #49466

**Backport**
This commit is contained in:
Stuart Tettemer 2019-12-06 15:08:05 -07:00 committed by GitHub
parent 8205cdd423
commit 17cda5b2c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 236 additions and 114 deletions

View File

@ -27,6 +27,7 @@ import org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.apache.lucene.util.AttributeSource;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
/**
* A predicate based on the current token in a TokenStream
@ -107,7 +108,7 @@ public abstract class AnalysisPredicateScript {
*/
public abstract boolean execute(Token token);
public interface Factory {
public interface Factory extends ScriptFactory {
AnalysisPredicateScript newInstance();
}

View File

@ -30,6 +30,7 @@ import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.indices.analysis.AnalysisModule;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTokenStreamTestCase;
import org.elasticsearch.test.IndexSettingsModule;
@ -63,7 +64,7 @@ public class PredicateTokenScriptFilterTests extends ESTokenStreamTestCase {
@SuppressWarnings("unchecked")
ScriptService scriptService = new ScriptService(indexSettings, Collections.emptyMap(), Collections.emptyMap()){
@Override
public <FactoryType> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
public <FactoryType extends ScriptFactory> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
assertEquals(context, AnalysisPredicateScript.CONTEXT);
assertEquals(new Script("my_script"), script);
return (FactoryType) factory;

View File

@ -30,6 +30,7 @@ import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.indices.analysis.AnalysisModule;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTokenStreamTestCase;
import org.elasticsearch.test.IndexSettingsModule;
@ -63,7 +64,7 @@ public class ScriptedConditionTokenFilterTests extends ESTokenStreamTestCase {
@SuppressWarnings("unchecked")
ScriptService scriptService = new ScriptService(indexSettings, Collections.emptyMap(), Collections.emptyMap()){
@Override
public <FactoryType> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
public <FactoryType extends ScriptFactory> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
assertEquals(context, AnalysisPredicateScript.CONTEXT);
assertEquals(new Script("token.getPosition() > 1"), script);
return (FactoryType) factory;

View File

@ -44,6 +44,7 @@ import org.elasticsearch.script.ScoreScript;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.TermsSetQueryScript;
import org.elasticsearch.search.lookup.SearchLookup;
@ -113,7 +114,12 @@ public class ExpressionScriptEngine implements ScriptEngine {
}
@Override
public <T> T compile(String scriptName, String scriptSource, ScriptContext<T> context, Map<String, String> params) {
public <T extends ScriptFactory> T compile(
String scriptName,
String scriptSource,
ScriptContext<T> context,
Map<String, String> params
) {
// classloader created here
final SecurityManager sm = System.getSecurityManager();
SpecialPermission.check();

View File

@ -32,6 +32,7 @@ import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.TemplateScript;
import java.io.Reader;
@ -64,7 +65,12 @@ public final class MustacheScriptEngine implements ScriptEngine {
* @return a compiled template object for later execution.
* */
@Override
public <T> T compile(String templateName, String templateSource, ScriptContext<T> context, Map<String, String> options) {
public <T extends ScriptFactory> T compile(
String templateName,
String templateSource,
ScriptContext<T> context,
Map<String, String> options
) {
if (context.instanceClazz.equals(TemplateScript.class) == false) {
throw new IllegalArgumentException("mustache engine does not know how to handle context [" + context.name + "]");
}

View File

@ -28,6 +28,7 @@ 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.ScriptFactory;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@ -66,7 +67,7 @@ public final class PainlessScriptEngine implements ScriptEngine {
*/
private static final AccessControlContext COMPILATION_CONTEXT;
/**
/*
* Setup the allowed permissions.
*/
static {
@ -122,7 +123,12 @@ public final class PainlessScriptEngine implements ScriptEngine {
}
@Override
public <T> T compile(String scriptName, String scriptSource, ScriptContext<T> context, Map<String, String> params) {
public <T extends ScriptFactory> T compile(
String scriptName,
String scriptSource,
ScriptContext<T> context,
Map<String, String> params
) {
Compiler compiler = contextsToCompilers.get(context);
// Check we ourselves are not being called by unprivileged code.
@ -162,12 +168,16 @@ public final class PainlessScriptEngine implements ScriptEngine {
* @param <T> The factory class.
* @return A factory class that will return script instances.
*/
private <T> Type generateStatefulFactory(Loader loader, ScriptContext<T> context, Set<String> extractedVariables) {
private <T extends ScriptFactory> Type generateStatefulFactory(
Loader loader,
ScriptContext<T> context,
Set<String> extractedVariables
) {
int classFrames = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
int classAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL;
String interfaceBase = Type.getType(context.statefulFactoryClazz).getInternalName();
String className = interfaceBase + "$StatefulFactory";
String classInterfaces[] = new String[] { interfaceBase };
String[] classInterfaces = new String[] { interfaceBase };
ClassWriter writer = new ClassWriter(classFrames);
writer.visit(WriterConstants.CLASS_VERSION, classAccess, className, null, OBJECT_TYPE.getInternalName(), classInterfaces);
@ -263,12 +273,17 @@ public final class PainlessScriptEngine implements ScriptEngine {
* @param <T> The factory class.
* @return A factory class that will return script instances.
*/
private <T> T generateFactory(Loader loader, ScriptContext<T> context, Set<String> extractedVariables, Type classType) {
private <T extends ScriptFactory> T generateFactory(
Loader loader,
ScriptContext<T> context,
Set<String> extractedVariables,
Type classType
) {
int classFrames = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
int classAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER| Opcodes.ACC_FINAL;
String interfaceBase = Type.getType(context.factoryClazz).getInternalName();
String className = interfaceBase + "$Factory";
String classInterfaces[] = new String[] { interfaceBase };
String[] classInterfaces = new String[] { interfaceBase };
ClassWriter writer = new ClassWriter(classFrames);
writer.visit(WriterConstants.CLASS_VERSION, classAccess, className, null, OBJECT_TYPE.getInternalName(), classInterfaces);

View File

@ -76,6 +76,7 @@ import org.elasticsearch.script.FilterScript;
import org.elasticsearch.script.ScoreScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.threadpool.ThreadPool;
@ -434,7 +435,7 @@ public class PainlessExecuteAction extends ActionType<PainlessExecuteAction.Resp
public abstract Object execute();
public interface Factory {
public interface Factory extends ScriptFactory {
PainlessTestScript newInstance(Map<String, Object> params);

View File

@ -21,6 +21,7 @@ package org.elasticsearch.painless;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import java.util.Collections;
import java.util.HashMap;
@ -65,7 +66,7 @@ public class BaseClassTests extends ScriptTestCase {
public abstract static class Gets {
public interface Factory {
public interface Factory extends ScriptFactory {
Gets newInstance(String testString, int testInt, Map<String, Object> params);
}
@ -111,7 +112,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class NoArgs {
public interface Factory {
public interface Factory extends ScriptFactory {
NoArgs newInstance();
}
@ -137,7 +138,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class OneArg {
public interface Factory {
public interface Factory extends ScriptFactory {
OneArg newInstance();
}
@ -154,7 +155,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class ArrayArg {
public interface Factory {
public interface Factory extends ScriptFactory {
ArrayArg newInstance();
}
@ -171,7 +172,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class PrimitiveArrayArg {
public interface Factory {
public interface Factory extends ScriptFactory {
PrimitiveArrayArg newInstance();
}
@ -188,7 +189,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class DefArrayArg {
public interface Factory {
public interface Factory extends ScriptFactory {
DefArrayArg newInstance();
}
@ -212,7 +213,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class ManyArgs {
public interface Factory {
public interface Factory extends ScriptFactory {
ManyArgs newInstance();
}
@ -251,7 +252,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class VarArgs {
public interface Factory {
public interface Factory extends ScriptFactory {
VarArgs newInstance();
}
@ -267,7 +268,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class DefaultMethods {
public interface Factory {
public interface Factory extends ScriptFactory {
DefaultMethods newInstance();
}
@ -301,7 +302,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class ReturnsVoid {
public interface Factory {
public interface Factory extends ScriptFactory {
ReturnsVoid newInstance();
}
@ -325,7 +326,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class ReturnsPrimitiveBoolean {
public interface Factory {
public interface Factory extends ScriptFactory {
ReturnsPrimitiveBoolean newInstance();
}
@ -391,20 +392,20 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class ReturnsPrimitiveInt {
public interface Factory {
public interface Factory extends ScriptFactory {
ReturnsPrimitiveInt newInstance();
}
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("returnsprimitiveint", Factory.class);
public static final String[] PARAMETERS = new String[] {};
public abstract int execute();
}
public void testReturnsPrimitiveInt() throws Exception {
assertEquals(1,
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt0", "1", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1,
assertEquals(1,
scriptEngine.compile("testReturnsPrimitiveInt1", "(int) 1L", ReturnsPrimitiveInt.CONTEXT, emptyMap())
.newInstance().execute());
assertEquals(1, scriptEngine.compile("testReturnsPrimitiveInt2", "(int) 1.1d", ReturnsPrimitiveInt.CONTEXT, emptyMap())
@ -455,12 +456,12 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class ReturnsPrimitiveFloat {
public interface Factory {
public interface Factory extends ScriptFactory {
ReturnsPrimitiveFloat newInstance();
}
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("returnsprimitivefloat", Factory.class);
public static final String[] PARAMETERS = new String[] {};
public abstract float execute();
}
@ -504,12 +505,12 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class ReturnsPrimitiveDouble {
public interface Factory {
public interface Factory extends ScriptFactory {
ReturnsPrimitiveDouble newInstance();
}
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("returnsprimitivedouble", Factory.class);
public static final String[] PARAMETERS = new String[] {};
public abstract double execute();
}
@ -567,7 +568,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class NoArgsConstant {
public interface Factory {
public interface Factory extends ScriptFactory {
NoArgsConstant newInstance();
}
@ -584,7 +585,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class WrongArgsConstant {
public interface Factory {
public interface Factory extends ScriptFactory {
WrongArgsConstant newInstance();
}
@ -602,7 +603,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class WrongLengthOfArgConstant {
public interface Factory {
public interface Factory extends ScriptFactory {
WrongLengthOfArgConstant newInstance();
}
@ -619,7 +620,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class UnknownArgType {
public interface Factory {
public interface Factory extends ScriptFactory {
UnknownArgType newInstance();
}
@ -636,7 +637,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class UnknownReturnType {
public interface Factory {
public interface Factory extends ScriptFactory {
UnknownReturnType newInstance();
}
@ -653,7 +654,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class UnknownArgTypeInArray {
public interface Factory {
public interface Factory extends ScriptFactory {
UnknownArgTypeInArray newInstance();
}
@ -670,7 +671,7 @@ public class BaseClassTests extends ScriptTestCase {
}
public abstract static class TwoExecuteMethods {
public interface Factory {
public interface Factory extends ScriptFactory {
TwoExecuteMethods newInstance();
}

View File

@ -2,6 +2,7 @@ package org.elasticsearch.painless;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import java.util.ArrayList;
import java.util.Collections;
@ -260,7 +261,7 @@ public class BasicStatementTests extends ScriptTestCase {
}
public abstract static class OneArg {
public interface Factory {
public interface Factory extends ScriptFactory {
OneArg newInstance();
}

View File

@ -23,6 +23,7 @@ import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.painless.spi.WhitelistInstanceBinding;
import org.elasticsearch.painless.spi.WhitelistLoader;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import java.util.ArrayList;
import java.util.Collections;
@ -89,7 +90,7 @@ public class BindingsTests extends ScriptTestCase {
public static final String[] PARAMETERS = { "test", "bound" };
public int getTestValue() {return 7;}
public abstract int execute(int test, int bound);
public interface Factory {
public interface Factory extends ScriptFactory {
BindingsTestScript newInstance();
}
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("bindings_test", Factory.class);

View File

@ -21,6 +21,7 @@ package org.elasticsearch.painless;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.TemplateScript;
import java.util.Collections;
@ -84,7 +85,7 @@ public class FactoryTests extends ScriptTestCase {
boolean needsD();
}
public interface Factory {
public interface Factory extends ScriptFactory {
StatefulFactory newFactory(int x, int y);
boolean needsTest();
@ -137,7 +138,7 @@ public class FactoryTests extends ScriptTestCase {
public static final String[] PARAMETERS = new String[] {"test"};
public abstract Object execute(int test);
public interface Factory {
public interface Factory extends ScriptFactory {
FactoryTestScript newInstance(Map<String, Object> params);
boolean needsTest();
@ -165,7 +166,7 @@ public class FactoryTests extends ScriptTestCase {
public static final String[] PARAMETERS = {};
public abstract Object execute();
public interface Factory {
public interface Factory extends ScriptFactory {
EmptyTestScript newInstance();
}

View File

@ -29,6 +29,7 @@ import org.elasticsearch.script.ScoreScript;
import org.elasticsearch.script.ScoreScript.LeafFactory;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.search.lookup.SearchLookup;
import java.io.IOException;
@ -39,16 +40,21 @@ import java.util.Map;
import java.util.Set;
/**
* An example script plugin that adds a {@link ScriptEngine} implementing expert scoring.
* An example script plugin that adds a {@link ScriptEngine}
* implementing expert scoring.
*/
public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
public ScriptEngine getScriptEngine(
Settings settings,
Collection<ScriptContext<?>> contexts
) {
return new MyExpertScriptEngine();
}
/** An example {@link ScriptEngine} that uses Lucene segment details to implement pure document frequency scoring. */
/** An example {@link ScriptEngine} that uses Lucene segment details to
* implement pure document frequency scoring. */
// tag::expert_engine
private static class MyExpertScriptEngine implements ScriptEngine {
@Override
@ -57,8 +63,12 @@ public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
}
@Override
public <T> T compile(String scriptName, String scriptSource,
ScriptContext<T> context, Map<String, String> params) {
public <T extends ScriptFactory> T compile(
String scriptName,
String scriptSource,
ScriptContext<T> context,
Map<String, String> params
) {
if (context.equals(ScoreScript.CONTEXT) == false) {
throw new IllegalArgumentException(getType()
+ " scripts cannot be used for context ["
@ -66,7 +76,7 @@ public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
}
// we use the script "source" as the script identifier
if ("pure_df".equals(scriptSource)) {
ScoreScript.Factory factory = PureDfLeafFactory::new;
ScoreScript.Factory factory = new PureDfFactory();
return context.factoryClazz.cast(factory);
}
throw new IllegalArgumentException("Unknown script name "
@ -83,6 +93,23 @@ public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
return Collections.singleton(ScoreScript.CONTEXT);
}
private static class PureDfFactory implements ScoreScript.Factory {
@Override
public boolean isResultDeterministic() {
// PureDfLeafFactory only uses deterministic APIs, this
// implies the results are cacheable.
return true;
}
@Override
public LeafFactory newFactory(
Map<String, Object> params,
SearchLookup lookup
) {
return new PureDfLeafFactory(params, lookup);
}
}
private static class PureDfLeafFactory implements LeafFactory {
private final Map<String, Object> params;
private final SearchLookup lookup;
@ -122,7 +149,9 @@ public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
*/
return new ScoreScript(params, lookup, context) {
@Override
public double execute(ExplanationHolder explanation) {
public double execute(
ExplanationHolder explanation
) {
return 0.0d;
}
};
@ -148,8 +177,8 @@ public class ExpertScriptPlugin extends Plugin implements ScriptPlugin {
public double execute(ExplanationHolder explanation) {
if (postings.docID() != currentDocid) {
/*
* advance moved past the current doc, so this doc
* has no occurrences of the term
* advance moved past the current doc, so this
* doc has no occurrences of the term
*/
return 0.0d;
}

View File

@ -93,7 +93,7 @@ public abstract class InnerHitContextBuilder {
if (innerHitBuilder.getScriptFields() != null) {
for (SearchSourceBuilder.ScriptField field : innerHitBuilder.getScriptFields()) {
QueryShardContext innerContext = innerHitsContext.getQueryShardContext();
FieldScript.Factory factory = innerContext.getScriptService().compile(field.script(), FieldScript.CONTEXT);
FieldScript.Factory factory = innerContext.compile(field.script(), FieldScript.CONTEXT);
FieldScript.LeafFactory fieldScript = factory.newFactory(field.script().getParams(), innerHitsContext.lookup());
innerHitsContext.scriptFields().add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
field.fieldName(), fieldScript, field.ignoreFailure()));

View File

@ -21,6 +21,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.queries.intervals.IntervalIterator;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
/**
* Base class for scripts used as interval filters, see {@link IntervalsSourceProvider.IntervalFilter}
@ -50,7 +51,7 @@ public abstract class IntervalFilterScript {
public abstract boolean execute(Interval interval);
public interface Factory {
public interface Factory extends ScriptFactory {
IntervalFilterScript newInstance();
}

View File

@ -717,7 +717,7 @@ public abstract class IntervalsSourceProvider implements NamedWriteable, ToXCont
public IntervalsSource filter(IntervalsSource input, QueryShardContext context, MappedFieldType fieldType) throws IOException {
if (script != null) {
IntervalFilterScript ifs = context.getScriptService().compile(script, IntervalFilterScript.CONTEXT).newInstance();
IntervalFilterScript ifs = context.compile(script, IntervalFilterScript.CONTEXT).newInstance();
return new ScriptFilterSource(input, script.getIdOrCode(), ifs);
}
IntervalsSource filterSource = filter.getSource(context, fieldType);

View File

@ -54,6 +54,9 @@ import org.elasticsearch.index.mapper.TextFieldMapper;
import org.elasticsearch.index.mapper.TypeFieldMapper;
import org.elasticsearch.index.query.support.NestedScope;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.transport.RemoteClusterAware;
@ -354,10 +357,13 @@ public class QueryShardContext extends QueryRewriteContext {
return indexSettings.getIndex();
}
/** Return the script service to allow compiling scripts. */
public final ScriptService getScriptService() {
failIfFrozen();
return scriptService;
/** Compile script using script service */
public <FactoryType extends ScriptFactory> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
FactoryType factory = scriptService.compile(script, context);
if (factory.isResultDeterministic() == false) {
failIfFrozen();
}
return factory;
}
/**

View File

@ -130,7 +130,7 @@ public class ScriptQueryBuilder extends AbstractQueryBuilder<ScriptQueryBuilder>
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
FilterScript.Factory factory = context.getScriptService().compile(script, FilterScript.CONTEXT);
FilterScript.Factory factory = context.compile(script, FilterScript.CONTEXT);
FilterScript.LeafFactory filterScript = factory.newFactory(script.getParams(), context.lookup());
return new ScriptQuery(script, filterScript);
}

View File

@ -40,6 +40,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.TermsSetQueryScript;
import java.io.IOException;
import java.util.ArrayList;
@ -47,7 +48,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.elasticsearch.script.TermsSetQueryScript;
public final class TermsSetQueryBuilder extends AbstractQueryBuilder<TermsSetQueryBuilder> {
@ -262,7 +262,7 @@ public final class TermsSetQueryBuilder extends AbstractQueryBuilder<TermsSetQue
IndexNumericFieldData fieldData = context.getForField(msmFieldType);
longValuesSource = new FieldValuesSource(fieldData);
} else if (minimumShouldMatchScript != null) {
TermsSetQueryScript.Factory factory = context.getScriptService().compile(minimumShouldMatchScript,
TermsSetQueryScript.Factory factory = context.compile(minimumShouldMatchScript,
TermsSetQueryScript.CONTEXT);
Map<String, Object> params = new HashMap<>();
params.putAll(minimumShouldMatchScript.getParams());

View File

@ -92,7 +92,7 @@ public class ScriptScoreFunctionBuilder extends ScoreFunctionBuilder<ScriptScore
@Override
protected ScoreFunction doToFunction(QueryShardContext context) {
try {
ScoreScript.Factory factory = context.getScriptService().compile(script, ScoreScript.CONTEXT);
ScoreScript.Factory factory = context.compile(script, ScoreScript.CONTEXT);
ScoreScript.LeafFactory searchScript = factory.newFactory(script.getParams(), context.lookup());
return new ScriptScoreFunction(script, searchScript,
context.index().getName(), context.getShardId(), context.indexVersionCreated());

View File

@ -170,7 +170,7 @@ public class ScriptScoreQueryBuilder extends AbstractQueryBuilder<ScriptScoreQue
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
ScoreScript.Factory factory = context.getScriptService().compile(script, ScoreScript.CONTEXT);
ScoreScript.Factory factory = context.compile(script, ScoreScript.CONTEXT);
ScoreScript.LeafFactory scoreScriptFactory = factory.newFactory(script.getParams(), context.lookup());
Query query = this.query.toQuery(context);
return new ScriptScoreQuery(query, script, scoreScriptFactory, minScore,

View File

@ -159,7 +159,7 @@ public abstract class AggregationScript implements ScorerAware {
/**
* A factory to construct stateful {@link AggregationScript} factories for a specific index.
*/
public interface Factory {
public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
}
}

View File

@ -48,7 +48,7 @@ public abstract class BucketAggregationScript {
public abstract Number execute();
public interface Factory {
public interface Factory extends ScriptFactory {
BucketAggregationScript newInstance(Map<String, Object> params);
}
}

View File

@ -48,7 +48,7 @@ public abstract class BucketAggregationSelectorScript {
public abstract boolean execute();
public interface Factory {
public interface Factory extends ScriptFactory {
BucketAggregationSelectorScript newInstance(Map<String, Object> params);
}
}

View File

@ -99,7 +99,7 @@ public abstract class FieldScript {
FieldScript newInstance(LeafReaderContext ctx) throws IOException;
}
public interface Factory {
public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
}

View File

@ -70,7 +70,7 @@ public abstract class FilterScript {
}
/** A factory to construct stateful {@link FilterScript} factories for a specific index. */
public interface Factory {
public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
}

View File

@ -45,7 +45,7 @@ public abstract class IngestConditionalScript {
public abstract boolean execute(Map<String, Object> ctx);
public interface Factory {
public interface Factory extends ScriptFactory {
IngestConditionalScript newInstance(Map<String, Object> params);
}
}

View File

@ -46,7 +46,7 @@ public abstract class IngestScript {
public abstract void execute(Map<String, Object> ctx);
public interface Factory {
public interface Factory extends ScriptFactory {
IngestScript newInstance(Map<String, Object> params);
}
}

View File

@ -54,7 +54,7 @@ public abstract class NumberSortScript extends AbstractSortScript {
/**
* A factory to construct stateful {@link NumberSortScript} factories for a specific index.
*/
public interface Factory {
public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
}
}

View File

@ -239,7 +239,7 @@ public abstract class ScoreScript {
}
/** A factory to construct stateful {@link ScoreScript} factories for a specific index. */
public interface Factory {
public interface Factory extends ScriptFactory {
ScoreScript.LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);

View File

@ -54,7 +54,7 @@ import java.lang.reflect.Method;
* If the variable name starts with an underscore, for example, {@code _score}, the needs method would
* be {@code boolean needs_score()}.
*/
public final class ScriptContext<FactoryType> {
public final class ScriptContext<FactoryType extends ScriptFactory> {
/** A unique identifier for this context. */
public final String name;

View File

@ -42,7 +42,12 @@ public interface ScriptEngine extends Closeable {
* @param params compile-time parameters (such as flags to the compiler)
* @return A compiled script of the FactoryType from {@link ScriptContext}
*/
<FactoryType> FactoryType compile(String name, String code, ScriptContext<FactoryType> context, Map<String, String> params);
<FactoryType extends ScriptFactory> FactoryType compile(
String name,
String code,
ScriptContext<FactoryType> context,
Map<String, String> params
);
@Override
default void close() throws IOException {}

View File

@ -0,0 +1,28 @@
/*
* 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;
/**
* Contains utility methods for compiled scripts without impacting concrete script signatures
*/
public interface ScriptFactory {
/** Returns {@code true} if the result of the script will be deterministic, {@code false} otherwise. */
default boolean isResultDeterministic() { return false; }
}

View File

@ -284,7 +284,7 @@ public class ScriptService implements Closeable, ClusterStateApplier {
*
* @return a compiled script which may be used to construct instances of a script for the given context
*/
public <FactoryType> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
public <FactoryType extends ScriptFactory> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
Objects.requireNonNull(script);
Objects.requireNonNull(context);

View File

@ -53,7 +53,7 @@ public class ScriptedMetricAggContexts {
public abstract void execute();
public interface Factory {
public interface Factory extends ScriptFactory {
InitScript newInstance(Map<String, Object> params, Map<String, Object> state);
}
@ -143,7 +143,7 @@ public class ScriptedMetricAggContexts {
MapScript newInstance(LeafReaderContext ctx);
}
public interface Factory {
public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, Map<String, Object> state, SearchLookup lookup);
}
@ -170,7 +170,7 @@ public class ScriptedMetricAggContexts {
public abstract Object execute();
public interface Factory {
public interface Factory extends ScriptFactory {
CombineScript newInstance(Map<String, Object> params, Map<String, Object> state);
}
@ -197,7 +197,7 @@ public class ScriptedMetricAggContexts {
public abstract Object execute();
public interface Factory {
public interface Factory extends ScriptFactory {
ReduceScript newInstance(Map<String, Object> params, List<Object> states);
}

View File

@ -32,7 +32,7 @@ public abstract class SignificantTermsHeuristicScoreScript {
public abstract double execute(Map<String, Object> params);
public interface Factory {
public interface Factory extends ScriptFactory {
SignificantTermsHeuristicScoreScript newInstance();
}
}

View File

@ -22,7 +22,7 @@ package org.elasticsearch.script;
import org.elasticsearch.index.similarity.ScriptedSimilarity;
/** A script that is used to build {@link ScriptedSimilarity} instances. */
public abstract class SimilarityScript {
public abstract class SimilarityScript {
/** Compute the score.
* @param weight weight computed by the {@link SimilarityWeightScript} if any, or 1.
@ -34,7 +34,7 @@ public abstract class SimilarityScript {
public abstract double execute(double weight, ScriptedSimilarity.Query query,
ScriptedSimilarity.Field field, ScriptedSimilarity.Term term, ScriptedSimilarity.Doc doc);
public interface Factory {
public interface Factory extends ScriptFactory {
SimilarityScript newInstance();
}

View File

@ -32,7 +32,7 @@ public abstract class SimilarityWeightScript {
public abstract double execute(ScriptedSimilarity.Query query, ScriptedSimilarity.Field field,
ScriptedSimilarity.Term term);
public interface Factory {
public interface Factory extends ScriptFactory {
SimilarityWeightScript newInstance();
}

View File

@ -45,7 +45,7 @@ public abstract class StringSortScript extends AbstractSortScript {
/**
* A factory to construct stateful {@link StringSortScript} factories for a specific index.
*/
public interface Factory {
public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
}
}

View File

@ -41,7 +41,7 @@ public abstract class TemplateScript {
/** Run a template and return the resulting string, encoded in utf8 bytes. */
public abstract String execute();
public interface Factory {
public interface Factory extends ScriptFactory {
TemplateScript newInstance(Map<String, Object> params);
}

View File

@ -112,7 +112,7 @@ public abstract class TermsSetQueryScript {
/**
* A factory to construct stateful {@link TermsSetQueryScript} factories for a specific index.
*/
public interface Factory {
public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
}
}

View File

@ -67,7 +67,7 @@ public abstract class UpdateScript {
public abstract void execute();
public interface Factory {
public interface Factory extends ScriptFactory {
UpdateScript newInstance(Map<String, Object> params, Map<String, Object> ctx);
}
}

View File

@ -101,7 +101,7 @@ public class ScriptHeuristic extends SignificanceHeuristic {
@Override
public SignificanceHeuristic rewrite(QueryShardContext queryShardContext) {
SignificantTermsHeuristicScoreScript.Factory compiledScript = queryShardContext.getScriptService().compile(script,
SignificantTermsHeuristicScoreScript.Factory compiledScript = queryShardContext.compile(script,
SignificantTermsHeuristicScoreScript.CONTEXT);
return new ExecutableScriptHeuristic(script, compiledScript.newInstance());
}

View File

@ -209,14 +209,14 @@ public class ScriptedMetricAggregationBuilder extends AbstractAggregationBuilder
ScriptedMetricAggContexts.InitScript.Factory compiledInitScript;
Map<String, Object> initScriptParams;
if (initScript != null) {
compiledInitScript = queryShardContext.getScriptService().compile(initScript, ScriptedMetricAggContexts.InitScript.CONTEXT);
compiledInitScript = queryShardContext.compile(initScript, ScriptedMetricAggContexts.InitScript.CONTEXT);
initScriptParams = initScript.getParams();
} else {
compiledInitScript = (p, a) -> null;
initScriptParams = Collections.emptyMap();
}
ScriptedMetricAggContexts.MapScript.Factory compiledMapScript = queryShardContext.getScriptService().compile(mapScript,
ScriptedMetricAggContexts.MapScript.Factory compiledMapScript = queryShardContext.compile(mapScript,
ScriptedMetricAggContexts.MapScript.CONTEXT);
Map<String, Object> mapScriptParams = mapScript.getParams();
@ -224,7 +224,7 @@ public class ScriptedMetricAggregationBuilder extends AbstractAggregationBuilder
ScriptedMetricAggContexts.CombineScript.Factory compiledCombineScript;
Map<String, Object> combineScriptParams;
compiledCombineScript = queryShardContext.getScriptService().compile(combineScript,
compiledCombineScript = queryShardContext.compile(combineScript,
ScriptedMetricAggContexts.CombineScript.CONTEXT);
combineScriptParams = combineScript.getParams();

View File

@ -592,7 +592,7 @@ public class TopHitsAggregationBuilder extends AbstractAggregationBuilder<TopHit
List<ScriptFieldsContext.ScriptField> fields = new ArrayList<>();
if (scriptFields != null) {
for (ScriptField field : scriptFields) {
FieldScript.Factory factory = queryShardContext.getScriptService().compile(field.script(), FieldScript.CONTEXT);
FieldScript.Factory factory = queryShardContext.compile(field.script(), FieldScript.CONTEXT);
FieldScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), queryShardContext.lookup());
fields.add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
field.fieldName(), searchScript, field.ignoreFailure()));

View File

@ -20,6 +20,7 @@
package org.elasticsearch.search.aggregations.pipeline;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import java.util.Map;
@ -35,7 +36,7 @@ public abstract class MovingFunctionScript {
*/
public abstract double execute(Map<String, Object> params, double[] values);
public interface Factory {
public interface Factory extends ScriptFactory {
MovingFunctionScript newInstance();
}

View File

@ -138,7 +138,7 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
if (script == null) {
return null;
} else {
AggregationScript.Factory factory = context.getScriptService().compile(script, AggregationScript.CONTEXT);
AggregationScript.Factory factory = context.compile(script, AggregationScript.CONTEXT);
return factory.newFactory(script.getParams(), context.lookup());
}
}

View File

@ -335,7 +335,7 @@ public class ScriptSortBuilder extends SortBuilder<ScriptSortBuilder> {
final IndexFieldData.XFieldComparatorSource fieldComparatorSource;
switch (type) {
case STRING:
final StringSortScript.Factory factory = context.getScriptService().compile(script, StringSortScript.CONTEXT);
final StringSortScript.Factory factory = context.compile(script, StringSortScript.CONTEXT);
final StringSortScript.LeafFactory searchScript = factory.newFactory(script.getParams(), context.lookup());
fieldComparatorSource = new BytesRefFieldComparatorSource(null, null, valueMode, nested) {
StringSortScript leafScript;
@ -364,7 +364,7 @@ public class ScriptSortBuilder extends SortBuilder<ScriptSortBuilder> {
};
break;
case NUMBER:
final NumberSortScript.Factory numberSortFactory = context.getScriptService().compile(script, NumberSortScript.CONTEXT);
final NumberSortScript.Factory numberSortFactory = context.compile(script, NumberSortScript.CONTEXT);
final NumberSortScript.LeafFactory numberSortScript = numberSortFactory.newFactory(script.getParams(), context.lookup());
fieldComparatorSource = new DoubleValuesComparatorSource(null, Double.MAX_VALUE, valueMode, nested) {
NumberSortScript leafScript;

View File

@ -634,7 +634,7 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
}
if (this.collateQuery != null) {
TemplateScript.Factory scriptFactory = context.getScriptService().compile(this.collateQuery, TemplateScript.CONTEXT);
TemplateScript.Factory scriptFactory = context.compile(this.collateQuery, TemplateScript.CONTEXT);
suggestionContext.setCollateQueryScript(scriptFactory);
if (this.collateParams != null) {
suggestionContext.setCollateScriptParams(this.collateParams);

View File

@ -35,6 +35,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.test.AbstractQueryTestCase;
@ -396,7 +397,7 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
ScriptService scriptService = new ScriptService(Settings.EMPTY, Collections.emptyMap(), Collections.emptyMap()){
@Override
@SuppressWarnings("unchecked")
public <FactoryType> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
public <FactoryType extends ScriptFactory> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
assertEquals(IntervalFilterScript.CONTEXT, context);
assertEquals(new Script("interval.start > 3"), script);
return (FactoryType) factory;

View File

@ -23,28 +23,28 @@ import org.elasticsearch.test.ESTestCase;
public class ScriptContextTests extends ESTestCase {
public interface TwoNewInstance {
public interface TwoNewInstance extends ScriptFactory {
String newInstance(int foo, int bar);
String newInstance(int foo);
interface StatefulFactory {
interface StatefulFactory extends ScriptFactory {
TwoNewInstance newFactory();
}
}
public interface TwoNewFactory {
public interface TwoNewFactory extends ScriptFactory {
String newFactory(int foo, int bar);
String newFactory(int foo);
}
public interface MissingNewInstance {
public interface MissingNewInstance extends ScriptFactory {
String typoNewInstanceMethod(int foo);
}
public interface DummyScript {
int execute(int foo);
interface Factory {
interface Factory extends ScriptFactory {
DummyScript newInstance();
}
}
@ -54,7 +54,7 @@ public class ScriptContextTests extends ESTestCase {
interface StatefulFactory {
DummyStatefulScript newInstance();
}
interface Factory {
interface Factory extends ScriptFactory {
StatefulFactory newFactory();
}
}

View File

@ -75,7 +75,7 @@ public class ScriptLanguagesInfoTests extends ESTestCase {
}
public interface MiscContext {
public interface MiscContext extends ScriptFactory {
void execute();
Object newInstance();
}

View File

@ -34,6 +34,7 @@ import org.elasticsearch.script.ScoreScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
@ -75,7 +76,12 @@ public class ExplainableScriptIT extends ESIntegTestCase {
}
@Override
public <T> T compile(String scriptName, String scriptSource, ScriptContext<T> context, Map<String, String> params) {
public <T extends ScriptFactory> T compile(
String scriptName,
String scriptSource,
ScriptContext<T> context,
Map<String, String> params
) {
assert scriptSource.equals("explainable_script");
assert context == ScoreScript.CONTEXT;
ScoreScript.Factory factory = (params1, lookup) -> new ScoreScript.LeafFactory() {

View File

@ -34,6 +34,7 @@ import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.TemplateScript;
import org.elasticsearch.search.suggest.phrase.DirectCandidateGeneratorBuilder;
import org.elasticsearch.search.suggest.phrase.Laplace;
@ -1137,7 +1138,12 @@ public class SuggestSearchIT extends ESIntegTestCase {
}
@Override
public <T> T compile(String scriptName, String scriptSource, ScriptContext<T> context, Map<String, String> params) {
public <T extends ScriptFactory> T compile(
String scriptName,
String scriptSource,
ScriptContext<T> context,
Map<String, String> params
) {
if (context.instanceClazz != TemplateScript.class) {
throw new UnsupportedOperationException();
}

View File

@ -23,6 +23,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.MockScriptEngine;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.TemplateScript;
@ -48,7 +49,7 @@ public class TestTemplateService extends ScriptService {
}
@Override
public <FactoryType> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
public <FactoryType extends ScriptFactory> FactoryType compile(Script script, ScriptContext<FactoryType> context) {
if (this.compilationException) {
throw new RuntimeException("could not compile script");
} else {

View File

@ -84,7 +84,7 @@ public class MockScriptEngine implements ScriptEngine {
}
@Override
public <T> T compile(String name, String source, ScriptContext<T> context, Map<String, String> params) {
public <T extends ScriptFactory> T compile(String name, String source, ScriptContext<T> context, Map<String, String> params) {
// Scripts are always resolved using the script's source. For inline scripts, it's easy because they don't have names and the
// source is always provided. For stored and file scripts, the source of the script must match the key of a predefined script.
Function<Map<String, Object>, Object> script = scripts.get(source);

View File

@ -39,7 +39,7 @@ public class MockMustacheScriptEngine extends MockScriptEngine {
}
@Override
public <T> T compile(String name, String script, ScriptContext<T> context, Map<String, String> params) {
public <T extends ScriptFactory> T compile(String name, String script, ScriptContext<T> context, Map<String, String> params) {
if (script.contains("{{") && script.contains("}}")) {
throw new IllegalArgumentException("Fix your test to not rely on mustache");
}

View File

@ -11,6 +11,7 @@ import org.elasticsearch.script.MockScriptPlugin;
import org.elasticsearch.script.ScoreScript;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptFactory;
import java.util.Collection;
import java.util.Collections;
@ -42,7 +43,7 @@ public class MockPainlessScriptEngine extends MockScriptEngine {
}
@Override
public <T> T compile(String name, String script, ScriptContext<T> context, Map<String, String> options) {
public <T extends ScriptFactory> T compile(String name, String script, ScriptContext<T> context, Map<String, String> options) {
if (context.instanceClazz.equals(ScoreScript.class)) {
return context.factoryClazz.cast(new MockScoreScript(p -> 0.0));
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.watcher.condition;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.Variables;
@ -37,7 +38,7 @@ public abstract class WatcherConditionScript {
return ctx;
}
public interface Factory {
public interface Factory extends ScriptFactory {
WatcherConditionScript newInstance(Map<String, Object> params, WatchExecutionContext watcherContext);
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.watcher.transform.script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptFactory;
import org.elasticsearch.xpack.core.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.core.watcher.watch.Payload;
import org.elasticsearch.xpack.watcher.support.Variables;
@ -38,7 +39,7 @@ public abstract class WatcherTransformScript {
return ctx;
}
public interface Factory {
public interface Factory extends ScriptFactory {
WatcherTransformScript newInstance(Map<String, Object> params, WatchExecutionContext watcherContext, Payload payload);
}