Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,23 @@
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;
}

Expand All @@ -55,15 +51,14 @@ public boolean needs_score() {
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
Expand All @@ -73,10 +68,7 @@ public boolean advanceExact(int doc) throws IOException {
});

@Override
public Object run() { return Double.valueOf(runAsDouble()); }

@Override
public double runAsDouble() {
public double execute() {
try {
return values.doubleValue();
} catch (Exception exception) {
Expand All @@ -92,24 +84,6 @@ public void setDocument(int d) {
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.
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
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;
Expand All @@ -48,11 +46,9 @@
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;
Expand All @@ -63,8 +59,9 @@
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 {

Expand Down Expand Up @@ -111,10 +108,7 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
}
}
});
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);
Expand Down Expand Up @@ -178,40 +172,6 @@ public Double execute() {
};
}

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)
Expand Down Expand Up @@ -315,13 +275,13 @@ private FieldScript.LeafFactory newFieldScript(Expression expr, SearchLookup loo
* 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) {
Expand All @@ -332,39 +292,37 @@ public void setDocument(int docid) {
}

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);
}

/**
Expand Down

This file was deleted.

Loading