Scripting: Convert script fields to use script context (#34164)
This commit removes the use of SearchScript for script fields and adds a new FieldScript.
This commit is contained in:
parent
7ab464807d
commit
222652dfce
|
@ -47,7 +47,7 @@ public class StoredScriptsIT extends ESRestHighLevelClientTestCase {
|
|||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||
|
||||
PutStoredScriptRequest request =
|
||||
new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));
|
||||
|
||||
GetStoredScriptRequest getRequest = new GetStoredScriptRequest("calculate-score");
|
||||
|
@ -66,7 +66,7 @@ public class StoredScriptsIT extends ESRestHighLevelClientTestCase {
|
|||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||
|
||||
PutStoredScriptRequest request =
|
||||
new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));
|
||||
|
||||
DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest(id);
|
||||
|
@ -89,7 +89,7 @@ public class StoredScriptsIT extends ESRestHighLevelClientTestCase {
|
|||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||
|
||||
PutStoredScriptRequest request =
|
||||
new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));
|
||||
|
||||
Map<String, Object> script = getAsMap("/_scripts/" + id);
|
||||
|
|
|
@ -307,7 +307,7 @@ public class StoredScriptsDocumentationIT extends ESRestHighLevelClientTestCase
|
|||
|
||||
private void putStoredScript(String id, StoredScriptSource scriptSource) throws IOException {
|
||||
PutStoredScriptRequest request =
|
||||
new PutStoredScriptRequest(id, "search", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource);
|
||||
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,10 @@ a customized value for each document in the results of a query.
|
|||
Contains the fields of the specified document where each field is a
|
||||
`List` of values.
|
||||
|
||||
{ref}/mapping-source-field.html[`ctx['_source']`] (`Map`)::
|
||||
{ref}/mapping-source-field.html[`params['_source']`] (`Map`, read-only)::
|
||||
Contains extracted JSON in a `Map` and `List` structure for the fields
|
||||
existing in a stored document.
|
||||
|
||||
`_score` (`double` read-only)::
|
||||
The original score of the specified document.
|
||||
|
||||
*Return*
|
||||
|
||||
`Object`::
|
||||
|
@ -28,4 +25,4 @@ a customized value for each document in the results of a query.
|
|||
|
||||
*API*
|
||||
|
||||
The standard <<painless-api-reference, Painless API>> is available.
|
||||
The standard <<painless-api-reference, Painless API>> is available.
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.expression;
|
||||
|
||||
import org.apache.lucene.expressions.Expression;
|
||||
import org.apache.lucene.expressions.SimpleBindings;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.script.GeneralScriptException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExpressionFieldScript implements FieldScript.LeafFactory {
|
||||
private final Expression exprScript;
|
||||
private final DoubleValuesSource source;
|
||||
|
||||
ExpressionFieldScript(Expression e, SimpleBindings b) {
|
||||
this.exprScript = e;
|
||||
this.source = exprScript.getDoubleValuesSource(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldScript newInstance(final LeafReaderContext leaf) throws IOException {
|
||||
return new FieldScript() {
|
||||
|
||||
// Fake the scorer until setScorer is called.
|
||||
DoubleValues values = source.getValues(leaf, null);
|
||||
|
||||
@Override
|
||||
public Object execute() {
|
||||
try {
|
||||
return values.doubleValue();
|
||||
} catch (Exception exception) {
|
||||
throw new GeneralScriptException("Error evaluating " + exprScript, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocument(int d) {
|
||||
try {
|
||||
values.advanceExact(d);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Can't advance to doc using " + exprScript, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ import org.elasticsearch.script.AggregationScript;
|
|||
import org.elasticsearch.script.BucketAggregationScript;
|
||||
import org.elasticsearch.script.BucketAggregationSelectorScript;
|
||||
import org.elasticsearch.script.ClassPermission;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.script.FilterScript;
|
||||
import org.elasticsearch.script.NumberSortScript;
|
||||
import org.elasticsearch.script.ScoreScript;
|
||||
|
@ -139,6 +140,9 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
|
|||
} else if (context.instanceClazz.equals(NumberSortScript.class)) {
|
||||
NumberSortScript.Factory factory = (p, lookup) -> newSortScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else if (context.instanceClazz.equals(FieldScript.class)) {
|
||||
FieldScript.Factory factory = (p, lookup) -> newFieldScript(expr, lookup, p);
|
||||
return context.factoryClazz.cast(factory);
|
||||
}
|
||||
throw new IllegalArgumentException("expression engine does not know how to handle script context [" + context.name + "]");
|
||||
}
|
||||
|
@ -289,6 +293,23 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
|
|||
return new ExpressionAggregationScript(expr, bindings, specialValue);
|
||||
}
|
||||
|
||||
private FieldScript.LeafFactory newFieldScript(Expression expr, SearchLookup lookup, @Nullable Map<String, Object> vars) {
|
||||
SimpleBindings bindings = new SimpleBindings();
|
||||
for (String variable : expr.variables) {
|
||||
try {
|
||||
if (vars != null && vars.containsKey(variable)) {
|
||||
bindFromParams(vars, bindings, variable);
|
||||
} else {
|
||||
final ValueSource valueSource = getDocValueSource(variable, lookup);
|
||||
bindings.add(variable, valueSource.asDoubleValuesSource());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw convertToScriptException("link error", expr.sourceText, variable, e);
|
||||
}
|
||||
}
|
||||
return new ExpressionFieldScript(expr, bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a hack for filter scripts, which must return booleans instead of doubles as expression do.
|
||||
* See https://github.com/elastic/elasticsearch/issues/26429.
|
||||
|
|
|
@ -24,10 +24,9 @@ import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
|
|||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType;
|
||||
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
|
||||
import org.elasticsearch.index.mapper.NumberFieldMapper;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
|
@ -35,12 +34,13 @@ import java.io.IOException;
|
|||
import java.text.ParseException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ExpressionTests extends ESTestCase {
|
||||
public class ExpressionFieldScriptTests extends ESTestCase {
|
||||
private ExpressionScriptEngine service;
|
||||
private SearchLookup lookup;
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class ExpressionTests extends ESTestCase {
|
|||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
NumberFieldType fieldType = new NumberFieldType(NumberType.DOUBLE);
|
||||
NumberFieldMapper.NumberFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.DOUBLE);
|
||||
MapperService mapperService = mock(MapperService.class);
|
||||
when(mapperService.fullName("field")).thenReturn(fieldType);
|
||||
when(mapperService.fullName("alias")).thenReturn(fieldType);
|
||||
|
@ -68,18 +68,11 @@ public class ExpressionTests extends ESTestCase {
|
|||
lookup = new SearchLookup(mapperService, ignored -> fieldData, null);
|
||||
}
|
||||
|
||||
private SearchScript.LeafFactory compile(String expression) {
|
||||
SearchScript.Factory factory = service.compile(null, expression, SearchScript.CONTEXT, Collections.emptyMap());
|
||||
private FieldScript.LeafFactory compile(String expression) {
|
||||
FieldScript.Factory factory = service.compile(null, expression, FieldScript.CONTEXT, Collections.emptyMap());
|
||||
return factory.newFactory(Collections.emptyMap(), lookup);
|
||||
}
|
||||
|
||||
public void testNeedsScores() {
|
||||
assertFalse(compile("1.2").needs_score());
|
||||
assertFalse(compile("doc['field'].value").needs_score());
|
||||
assertTrue(compile("1/_score").needs_score());
|
||||
assertTrue(compile("doc['field'].value * _score").needs_score());
|
||||
}
|
||||
|
||||
public void testCompileError() {
|
||||
ScriptException e = expectThrows(ScriptException.class, () -> {
|
||||
compile("doc['field'].value * *@#)(@$*@#$ + 4");
|
||||
|
@ -95,18 +88,18 @@ public class ExpressionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testFieldAccess() throws IOException {
|
||||
SearchScript script = compile("doc['field'].value").newInstance(null);
|
||||
FieldScript script = compile("doc['field'].value").newInstance(null);
|
||||
script.setDocument(1);
|
||||
|
||||
double result = script.runAsDouble();
|
||||
assertEquals(2.718, result, 0.0);
|
||||
Object result = script.execute();
|
||||
assertThat(result, equalTo(2.718));
|
||||
}
|
||||
|
||||
public void testFieldAccessWithFieldAlias() throws IOException {
|
||||
SearchScript script = compile("doc['alias'].value").newInstance(null);
|
||||
FieldScript script = compile("doc['alias'].value").newInstance(null);
|
||||
script.setDocument(1);
|
||||
|
||||
double result = script.runAsDouble();
|
||||
assertEquals(2.718, result, 0.0);
|
||||
Object result = script.execute();
|
||||
assertThat(result, equalTo(2.718));
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ public class StoredExpressionTests extends ESIntegTestCase {
|
|||
.setIndices("test").setTypes("scriptTest").get();
|
||||
fail("search script should have been rejected");
|
||||
} catch(Exception e) {
|
||||
assertThat(e.toString(), containsString("cannot execute scripts using [search] context"));
|
||||
assertThat(e.toString(), containsString("cannot execute scripts using [field] context"));
|
||||
}
|
||||
try {
|
||||
client().prepareSearch("test")
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
|
|||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
|
@ -67,7 +66,6 @@ public abstract class ScriptTestCase extends ESTestCase {
|
|||
*/
|
||||
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
||||
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
||||
contexts.put(SearchScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
contexts.put(PainlessTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||
return contexts;
|
||||
}
|
||||
|
|
|
@ -38,12 +38,12 @@
|
|||
catch: bad_request
|
||||
put_script:
|
||||
id: "1"
|
||||
context: "search"
|
||||
context: "score"
|
||||
body: { "script": {"lang": "painless", "source": "_score * foo bar + doc['myParent.weight'].value"} }
|
||||
|
||||
- do:
|
||||
catch: /compile error/
|
||||
put_script:
|
||||
id: "1"
|
||||
context: "search"
|
||||
context: "score"
|
||||
body: { "script": {"lang": "painless", "source": "_score * foo bar + doc['myParent.weight'].value"} }
|
||||
|
|
|
@ -19,15 +19,15 @@
|
|||
|
||||
package org.elasticsearch.example.painlesswhitelist;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.painless.spi.PainlessExtension;
|
||||
import org.elasticsearch.painless.spi.Whitelist;
|
||||
import org.elasticsearch.painless.spi.WhitelistLoader;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** An extension of painless which adds a whitelist. */
|
||||
public class ExampleWhitelistExtension implements PainlessExtension {
|
||||
|
@ -37,6 +37,6 @@ public class ExampleWhitelistExtension implements PainlessExtension {
|
|||
|
||||
@Override
|
||||
public Map<ScriptContext<?>, List<Whitelist>> getContextWhitelists() {
|
||||
return Collections.singletonMap(SearchScript.CONTEXT, Collections.singletonList(WHITELIST));
|
||||
return Collections.singletonMap(FieldScript.CONTEXT, Collections.singletonList(WHITELIST));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.DocValueFieldsContext;
|
||||
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
|
||||
|
@ -88,10 +88,10 @@ public abstract class InnerHitContextBuilder {
|
|||
if (innerHitBuilder.getScriptFields() != null) {
|
||||
for (SearchSourceBuilder.ScriptField field : innerHitBuilder.getScriptFields()) {
|
||||
QueryShardContext innerContext = innerHitsContext.getQueryShardContext();
|
||||
SearchScript.Factory factory = innerContext.getScriptService().compile(field.script(), SearchScript.CONTEXT);
|
||||
SearchScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), innerHitsContext.lookup());
|
||||
FieldScript.Factory factory = innerContext.getScriptService().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(), searchScript, field.ignoreFailure()));
|
||||
field.fieldName(), fieldScript, field.ignoreFailure()));
|
||||
}
|
||||
}
|
||||
if (innerHitBuilder.getFetchSourceContext() != null) {
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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.elasticsearch.index.fielddata.ScriptDocValues;
|
||||
import org.elasticsearch.search.lookup.LeafSearchLookup;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A script to produce dynamic values for return fields.
|
||||
*/
|
||||
public abstract class FieldScript {
|
||||
|
||||
public static final String[] PARAMETERS = {};
|
||||
|
||||
private static final Map<String, String> DEPRECATIONS;
|
||||
|
||||
static {
|
||||
Map<String, String> deprecations = new HashMap<>();
|
||||
deprecations.put(
|
||||
"doc",
|
||||
"Accessing variable [doc] via [params.doc] from within a field script " +
|
||||
"is deprecated in favor of directly accessing [doc]."
|
||||
);
|
||||
deprecations.put(
|
||||
"_doc",
|
||||
"Accessing variable [doc] via [params._doc] from within a field script " +
|
||||
"is deprecated in favor of directly accessing [doc]."
|
||||
);
|
||||
DEPRECATIONS = Collections.unmodifiableMap(deprecations);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
public FieldScript(Map<String, Object> params, SearchLookup lookup, LeafReaderContext leafContext) {
|
||||
this.leafLookup = lookup.getLeafSearchLookup(leafContext);
|
||||
params = new HashMap<>(params);
|
||||
params.putAll(leafLookup.asMap());
|
||||
this.params = new ParameterMap(params, DEPRECATIONS);
|
||||
}
|
||||
|
||||
// for expression engine
|
||||
protected FieldScript() {
|
||||
params = null;
|
||||
leafLookup = null;
|
||||
}
|
||||
|
||||
public abstract Object execute();
|
||||
|
||||
/** The leaf lookup for the Lucene segment this script was created for. */
|
||||
protected final LeafSearchLookup getLeafLookup() {
|
||||
return leafLookup;
|
||||
}
|
||||
|
||||
/** Return the parameters for this script. */
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
/** The doc lookup for the Lucene segment this script was created for. */
|
||||
public final Map<String, ScriptDocValues<?>> getDoc() {
|
||||
return leafLookup.doc();
|
||||
}
|
||||
|
||||
/** Set the current document to run the script on next. */
|
||||
public void setDocument(int docid) {
|
||||
leafLookup.setDocument(docid);
|
||||
}
|
||||
|
||||
/** A factory to construct {@link SearchScript} instances. */
|
||||
public interface LeafFactory {
|
||||
FieldScript newInstance(LeafReaderContext ctx) throws IOException;
|
||||
}
|
||||
|
||||
public interface Factory {
|
||||
LeafFactory newFactory(Map<String, Object> params, SearchLookup lookup);
|
||||
}
|
||||
|
||||
/** The context used to compile {@link FieldScript} factories. */
|
||||
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("field", Factory.class);
|
||||
}
|
|
@ -40,7 +40,7 @@ public class ScriptModule {
|
|||
public static final Map<String, ScriptContext<?>> CORE_CONTEXTS;
|
||||
static {
|
||||
CORE_CONTEXTS = Stream.of(
|
||||
SearchScript.CONTEXT,
|
||||
FieldScript.CONTEXT,
|
||||
AggregationScript.CONTEXT,
|
||||
ScoreScript.CONTEXT,
|
||||
NumberSortScript.CONTEXT,
|
||||
|
|
|
@ -58,8 +58,8 @@ import org.elasticsearch.index.shard.SearchOperationListener;
|
|||
import org.elasticsearch.indices.IndicesService;
|
||||
import org.elasticsearch.indices.cluster.IndicesClusterStateService.AllocatedIndices.IndexRemovalReason;
|
||||
import org.elasticsearch.node.ResponseCollectorService;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.aggregations.AggregationInitializationException;
|
||||
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
||||
import org.elasticsearch.search.aggregations.InternalAggregation;
|
||||
|
@ -863,8 +863,8 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
|
|||
+ IndexSettings.MAX_SCRIPT_FIELDS_SETTING.getKey() + "] index level setting.");
|
||||
}
|
||||
for (org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField field : source.scriptFields()) {
|
||||
SearchScript.Factory factory = scriptService.compile(field.script(), SearchScript.CONTEXT);
|
||||
SearchScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), context.lookup());
|
||||
FieldScript.Factory factory = scriptService.compile(field.script(), FieldScript.CONTEXT);
|
||||
FieldScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), context.lookup());
|
||||
context.scriptFields().add(new ScriptField(field.fieldName(), searchScript, field.ignoreFailure()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||
import org.elasticsearch.search.aggregations.AggregationBuilder;
|
||||
import org.elasticsearch.search.aggregations.AggregationInitializationException;
|
||||
|
@ -566,8 +566,8 @@ public class TopHitsAggregationBuilder extends AbstractAggregationBuilder<TopHit
|
|||
if (scriptFields != null) {
|
||||
for (ScriptField field : scriptFields) {
|
||||
QueryShardContext shardContext = context.getQueryShardContext();
|
||||
SearchScript.Factory factory = shardContext.getScriptService().compile(field.script(), SearchScript.CONTEXT);
|
||||
SearchScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), shardContext.lookup());
|
||||
FieldScript.Factory factory = shardContext.getScriptService().compile(field.script(), FieldScript.CONTEXT);
|
||||
FieldScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), shardContext.lookup());
|
||||
fields.add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
|
||||
field.fieldName(), searchScript, field.ignoreFailure()));
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.elasticsearch.search.fetch.subphase;
|
||||
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -28,10 +28,10 @@ public class ScriptFieldsContext {
|
|||
|
||||
public static class ScriptField {
|
||||
private final String name;
|
||||
private final SearchScript.LeafFactory script;
|
||||
private final FieldScript.LeafFactory script;
|
||||
private final boolean ignoreException;
|
||||
|
||||
public ScriptField(String name, SearchScript.LeafFactory script, boolean ignoreException) {
|
||||
public ScriptField(String name, FieldScript.LeafFactory script, boolean ignoreException) {
|
||||
this.name = name;
|
||||
this.script = script;
|
||||
this.ignoreException = ignoreException;
|
||||
|
@ -41,7 +41,7 @@ public class ScriptFieldsContext {
|
|||
return name;
|
||||
}
|
||||
|
||||
public SearchScript.LeafFactory script() {
|
||||
public FieldScript.LeafFactory script() {
|
||||
return this.script;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.apache.lucene.index.LeafReaderContext;
|
|||
import org.apache.lucene.index.ReaderUtil;
|
||||
import org.elasticsearch.common.document.DocumentField;
|
||||
import org.elasticsearch.common.util.CollectionUtils;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.fetch.FetchSubPhase;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
|
@ -49,7 +49,7 @@ public final class ScriptFieldsFetchSubPhase implements FetchSubPhase {
|
|||
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
|
||||
|
||||
int lastReaderId = -1;
|
||||
SearchScript[] leafScripts = null;
|
||||
FieldScript[] leafScripts = null;
|
||||
List<ScriptFieldsContext.ScriptField> scriptFields = context.scriptFields().fields();
|
||||
final IndexReader reader = context.searcher().getIndexReader();
|
||||
for (SearchHit hit : hits) {
|
||||
|
@ -64,7 +64,7 @@ public final class ScriptFieldsFetchSubPhase implements FetchSubPhase {
|
|||
leafScripts[i].setDocument(docId);
|
||||
final Object value;
|
||||
try {
|
||||
value = leafScripts[i].run();
|
||||
value = leafScripts[i].execute();
|
||||
CollectionUtils.ensureNoSelfReferences(value, "ScriptFieldsFetchSubPhase leaf script " + i);
|
||||
} catch (RuntimeException e) {
|
||||
if (scriptFields.get(i).ignoreException()) {
|
||||
|
@ -91,9 +91,9 @@ public final class ScriptFieldsFetchSubPhase implements FetchSubPhase {
|
|||
}
|
||||
}
|
||||
|
||||
private SearchScript[] createLeafScripts(LeafReaderContext context,
|
||||
List<ScriptFieldsContext.ScriptField> scriptFields) {
|
||||
SearchScript[] scripts = new SearchScript[scriptFields.size()];
|
||||
private FieldScript[] createLeafScripts(LeafReaderContext context,
|
||||
List<ScriptFieldsContext.ScriptField> scriptFields) {
|
||||
FieldScript[] scripts = new FieldScript[scriptFields.size()];
|
||||
for (int i = 0; i < scripts.length; i++) {
|
||||
try {
|
||||
scripts[i] = scriptFields.get(i).script().newInstance(context);
|
||||
|
|
|
@ -150,22 +150,22 @@ public class ScriptServiceTests extends ESTestCase {
|
|||
public void testInlineScriptCompiledOnceCache() throws IOException {
|
||||
buildScriptService(Settings.EMPTY);
|
||||
Script script = new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap());
|
||||
SearchScript.Factory factoryScript1 = scriptService.compile(script, SearchScript.CONTEXT);
|
||||
SearchScript.Factory factoryScript2 = scriptService.compile(script, SearchScript.CONTEXT);
|
||||
FieldScript.Factory factoryScript1 = scriptService.compile(script, FieldScript.CONTEXT);
|
||||
FieldScript.Factory factoryScript2 = scriptService.compile(script, FieldScript.CONTEXT);
|
||||
assertThat(factoryScript1, sameInstance(factoryScript2));
|
||||
}
|
||||
|
||||
public void testAllowAllScriptTypeSettings() throws IOException {
|
||||
buildScriptService(Settings.EMPTY);
|
||||
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||
assertCompileAccepted(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||
assertCompileAccepted(null, "script", ScriptType.STORED, FieldScript.CONTEXT);
|
||||
}
|
||||
|
||||
public void testAllowAllScriptContextSettings() throws IOException {
|
||||
buildScriptService(Settings.EMPTY);
|
||||
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, AggregationScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, UpdateScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, IngestScript.CONTEXT);
|
||||
|
@ -176,16 +176,16 @@ public class ScriptServiceTests extends ESTestCase {
|
|||
builder.put("script.allowed_types", "inline");
|
||||
buildScriptService(builder.build());
|
||||
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||
assertCompileRejected(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||
assertCompileRejected(null, "script", ScriptType.STORED, FieldScript.CONTEXT);
|
||||
}
|
||||
|
||||
public void testAllowSomeScriptContextSettings() throws IOException {
|
||||
Settings.Builder builder = Settings.builder();
|
||||
builder.put("script.allowed_contexts", "search, aggs");
|
||||
builder.put("script.allowed_contexts", "field, aggs");
|
||||
buildScriptService(builder.build());
|
||||
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, AggregationScript.CONTEXT);
|
||||
assertCompileRejected("painless", "script", ScriptType.INLINE, UpdateScript.CONTEXT);
|
||||
}
|
||||
|
@ -195,8 +195,8 @@ public class ScriptServiceTests extends ESTestCase {
|
|||
builder.put("script.allowed_types", "none");
|
||||
buildScriptService(builder.build());
|
||||
|
||||
assertCompileRejected("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||
assertCompileRejected(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
||||
assertCompileRejected("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||
assertCompileRejected(null, "script", ScriptType.STORED, FieldScript.CONTEXT);
|
||||
}
|
||||
|
||||
public void testAllowNoScriptContextSettings() throws IOException {
|
||||
|
@ -204,7 +204,7 @@ public class ScriptServiceTests extends ESTestCase {
|
|||
builder.put("script.allowed_contexts", "none");
|
||||
buildScriptService(builder.build());
|
||||
|
||||
assertCompileRejected("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
||||
assertCompileRejected("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||
assertCompileRejected("painless", "script", ScriptType.INLINE, AggregationScript.CONTEXT);
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,8 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
|||
scripts.put("doc['num1'].value * factor", vars -> {
|
||||
Map<?, ?> doc = (Map) vars.get("doc");
|
||||
ScriptDocValues.Doubles num1 = (ScriptDocValues.Doubles) doc.get("num1");
|
||||
Double factor = (Double) vars.get("factor");
|
||||
Map<String, Object> params = (Map<String, Object>) vars.get("params");
|
||||
Double factor = (Double) params.get("factor");
|
||||
return num1.getValue() * factor;
|
||||
});
|
||||
|
||||
|
|
|
@ -93,6 +93,17 @@ public class MockScriptEngine implements ScriptEngine {
|
|||
if (context.instanceClazz.equals(SearchScript.class)) {
|
||||
SearchScript.Factory factory = mockCompiled::createSearchScript;
|
||||
return context.factoryClazz.cast(factory);
|
||||
} else 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(TermsSetQueryScript.class)) {
|
||||
TermsSetQueryScript.Factory factory = (parameters, lookup) -> (TermsSetQueryScript.LeafFactory) ctx
|
||||
-> new TermsSetQueryScript(parameters, lookup, ctx) {
|
||||
|
@ -276,6 +287,12 @@ public class MockScriptEngine implements ScriptEngine {
|
|||
throw new IllegalArgumentException("mock script engine does not know how to handle context [" + context.name + "]");
|
||||
}
|
||||
|
||||
private Map<String, Object> createVars(Map<String, Object> params) {
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("params", params);
|
||||
return vars;
|
||||
}
|
||||
|
||||
public class MockCompiledScript {
|
||||
|
||||
private final String name;
|
||||
|
|
|
@ -10,15 +10,15 @@ import org.elasticsearch.painless.spi.Whitelist;
|
|||
import org.elasticsearch.painless.spi.WhitelistLoader;
|
||||
import org.elasticsearch.script.AggregationScript;
|
||||
import org.elasticsearch.script.BucketAggregationSelectorScript;
|
||||
import org.elasticsearch.script.FieldScript;
|
||||
import org.elasticsearch.script.FilterScript;
|
||||
import org.elasticsearch.script.NumberSortScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.script.StringSortScript;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.elasticsearch.script.NumberSortScript;
|
||||
import org.elasticsearch.script.StringSortScript;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class SqlPainlessExtension implements PainlessExtension {
|
|||
List<Whitelist> list = singletonList(WHITELIST);
|
||||
whitelist.put(FilterScript.CONTEXT, list);
|
||||
whitelist.put(AggregationScript.CONTEXT, list);
|
||||
whitelist.put(SearchScript.CONTEXT, list);
|
||||
whitelist.put(FieldScript.CONTEXT, list);
|
||||
whitelist.put(NumberSortScript.CONTEXT, list);
|
||||
whitelist.put(StringSortScript.CONTEXT, list);
|
||||
whitelist.put(BucketAggregationSelectorScript.CONTEXT, list);
|
||||
|
|
Loading…
Reference in New Issue