|
@@ -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);
|
|
|
}
|
|
|
|
|
|
/**
|