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()));
|
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||||
|
|
||||||
PutStoredScriptRequest request =
|
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));
|
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));
|
||||||
|
|
||||||
GetStoredScriptRequest getRequest = new GetStoredScriptRequest("calculate-score");
|
GetStoredScriptRequest getRequest = new GetStoredScriptRequest("calculate-score");
|
||||||
|
@ -66,7 +66,7 @@ public class StoredScriptsIT extends ESRestHighLevelClientTestCase {
|
||||||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||||
|
|
||||||
PutStoredScriptRequest request =
|
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));
|
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));
|
||||||
|
|
||||||
DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest(id);
|
DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest(id);
|
||||||
|
@ -89,7 +89,7 @@ public class StoredScriptsIT extends ESRestHighLevelClientTestCase {
|
||||||
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()));
|
||||||
|
|
||||||
PutStoredScriptRequest request =
|
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));
|
assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync));
|
||||||
|
|
||||||
Map<String, Object> script = getAsMap("/_scripts/" + id);
|
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 {
|
private void putStoredScript(String id, StoredScriptSource scriptSource) throws IOException {
|
||||||
PutStoredScriptRequest request =
|
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));
|
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
|
Contains the fields of the specified document where each field is a
|
||||||
`List` of values.
|
`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
|
Contains extracted JSON in a `Map` and `List` structure for the fields
|
||||||
existing in a stored document.
|
existing in a stored document.
|
||||||
|
|
||||||
`_score` (`double` read-only)::
|
|
||||||
The original score of the specified document.
|
|
||||||
|
|
||||||
*Return*
|
*Return*
|
||||||
|
|
||||||
`Object`::
|
`Object`::
|
||||||
|
@ -28,4 +25,4 @@ a customized value for each document in the results of a query.
|
||||||
|
|
||||||
*API*
|
*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.BucketAggregationScript;
|
||||||
import org.elasticsearch.script.BucketAggregationSelectorScript;
|
import org.elasticsearch.script.BucketAggregationSelectorScript;
|
||||||
import org.elasticsearch.script.ClassPermission;
|
import org.elasticsearch.script.ClassPermission;
|
||||||
|
import org.elasticsearch.script.FieldScript;
|
||||||
import org.elasticsearch.script.FilterScript;
|
import org.elasticsearch.script.FilterScript;
|
||||||
import org.elasticsearch.script.NumberSortScript;
|
import org.elasticsearch.script.NumberSortScript;
|
||||||
import org.elasticsearch.script.ScoreScript;
|
import org.elasticsearch.script.ScoreScript;
|
||||||
|
@ -139,6 +140,9 @@ public class ExpressionScriptEngine extends AbstractComponent implements ScriptE
|
||||||
} else if (context.instanceClazz.equals(NumberSortScript.class)) {
|
} else if (context.instanceClazz.equals(NumberSortScript.class)) {
|
||||||
NumberSortScript.Factory factory = (p, lookup) -> newSortScript(expr, lookup, p);
|
NumberSortScript.Factory factory = (p, lookup) -> newSortScript(expr, lookup, p);
|
||||||
return context.factoryClazz.cast(factory);
|
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 + "]");
|
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);
|
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.
|
* 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.
|
* 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.IndexNumericFieldData;
|
||||||
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType;
|
import org.elasticsearch.index.mapper.NumberFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
|
import org.elasticsearch.script.FieldScript;
|
||||||
import org.elasticsearch.script.ScriptException;
|
import org.elasticsearch.script.ScriptException;
|
||||||
import org.elasticsearch.script.SearchScript;
|
|
||||||
import org.elasticsearch.search.lookup.SearchLookup;
|
import org.elasticsearch.search.lookup.SearchLookup;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
|
@ -35,12 +34,13 @@ import java.io.IOException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.mockito.Matchers.anyInt;
|
import static org.mockito.Matchers.anyInt;
|
||||||
import static org.mockito.Matchers.anyObject;
|
import static org.mockito.Matchers.anyObject;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class ExpressionTests extends ESTestCase {
|
public class ExpressionFieldScriptTests extends ESTestCase {
|
||||||
private ExpressionScriptEngine service;
|
private ExpressionScriptEngine service;
|
||||||
private SearchLookup lookup;
|
private SearchLookup lookup;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public class ExpressionTests extends ESTestCase {
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
|
||||||
NumberFieldType fieldType = new NumberFieldType(NumberType.DOUBLE);
|
NumberFieldMapper.NumberFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.DOUBLE);
|
||||||
MapperService mapperService = mock(MapperService.class);
|
MapperService mapperService = mock(MapperService.class);
|
||||||
when(mapperService.fullName("field")).thenReturn(fieldType);
|
when(mapperService.fullName("field")).thenReturn(fieldType);
|
||||||
when(mapperService.fullName("alias")).thenReturn(fieldType);
|
when(mapperService.fullName("alias")).thenReturn(fieldType);
|
||||||
|
@ -68,18 +68,11 @@ public class ExpressionTests extends ESTestCase {
|
||||||
lookup = new SearchLookup(mapperService, ignored -> fieldData, null);
|
lookup = new SearchLookup(mapperService, ignored -> fieldData, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SearchScript.LeafFactory compile(String expression) {
|
private FieldScript.LeafFactory compile(String expression) {
|
||||||
SearchScript.Factory factory = service.compile(null, expression, SearchScript.CONTEXT, Collections.emptyMap());
|
FieldScript.Factory factory = service.compile(null, expression, FieldScript.CONTEXT, Collections.emptyMap());
|
||||||
return factory.newFactory(Collections.emptyMap(), lookup);
|
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() {
|
public void testCompileError() {
|
||||||
ScriptException e = expectThrows(ScriptException.class, () -> {
|
ScriptException e = expectThrows(ScriptException.class, () -> {
|
||||||
compile("doc['field'].value * *@#)(@$*@#$ + 4");
|
compile("doc['field'].value * *@#)(@$*@#$ + 4");
|
||||||
|
@ -95,18 +88,18 @@ public class ExpressionTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFieldAccess() throws IOException {
|
public void testFieldAccess() throws IOException {
|
||||||
SearchScript script = compile("doc['field'].value").newInstance(null);
|
FieldScript script = compile("doc['field'].value").newInstance(null);
|
||||||
script.setDocument(1);
|
script.setDocument(1);
|
||||||
|
|
||||||
double result = script.runAsDouble();
|
Object result = script.execute();
|
||||||
assertEquals(2.718, result, 0.0);
|
assertThat(result, equalTo(2.718));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFieldAccessWithFieldAlias() throws IOException {
|
public void testFieldAccessWithFieldAlias() throws IOException {
|
||||||
SearchScript script = compile("doc['alias'].value").newInstance(null);
|
FieldScript script = compile("doc['alias'].value").newInstance(null);
|
||||||
script.setDocument(1);
|
script.setDocument(1);
|
||||||
|
|
||||||
double result = script.runAsDouble();
|
Object result = script.execute();
|
||||||
assertEquals(2.718, result, 0.0);
|
assertThat(result, equalTo(2.718));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -70,7 +70,7 @@ public class StoredExpressionTests extends ESIntegTestCase {
|
||||||
.setIndices("test").setTypes("scriptTest").get();
|
.setIndices("test").setTypes("scriptTest").get();
|
||||||
fail("search script should have been rejected");
|
fail("search script should have been rejected");
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
assertThat(e.toString(), containsString("cannot execute scripts using [search] context"));
|
assertThat(e.toString(), containsString("cannot execute scripts using [field] context"));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
client().prepareSearch("test")
|
client().prepareSearch("test")
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.elasticsearch.painless.lookup.PainlessLookupBuilder;
|
||||||
import org.elasticsearch.painless.spi.Whitelist;
|
import org.elasticsearch.painless.spi.Whitelist;
|
||||||
import org.elasticsearch.script.ScriptContext;
|
import org.elasticsearch.script.ScriptContext;
|
||||||
import org.elasticsearch.script.ScriptException;
|
import org.elasticsearch.script.ScriptException;
|
||||||
import org.elasticsearch.script.SearchScript;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
@ -67,7 +66,6 @@ public abstract class ScriptTestCase extends ESTestCase {
|
||||||
*/
|
*/
|
||||||
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
protected Map<ScriptContext<?>, List<Whitelist>> scriptContexts() {
|
||||||
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
Map<ScriptContext<?>, List<Whitelist>> contexts = new HashMap<>();
|
||||||
contexts.put(SearchScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
|
||||||
contexts.put(PainlessTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
contexts.put(PainlessTestScript.CONTEXT, Whitelist.BASE_WHITELISTS);
|
||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,12 +38,12 @@
|
||||||
catch: bad_request
|
catch: bad_request
|
||||||
put_script:
|
put_script:
|
||||||
id: "1"
|
id: "1"
|
||||||
context: "search"
|
context: "score"
|
||||||
body: { "script": {"lang": "painless", "source": "_score * foo bar + doc['myParent.weight'].value"} }
|
body: { "script": {"lang": "painless", "source": "_score * foo bar + doc['myParent.weight'].value"} }
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
catch: /compile error/
|
catch: /compile error/
|
||||||
put_script:
|
put_script:
|
||||||
id: "1"
|
id: "1"
|
||||||
context: "search"
|
context: "score"
|
||||||
body: { "script": {"lang": "painless", "source": "_score * foo bar + doc['myParent.weight'].value"} }
|
body: { "script": {"lang": "painless", "source": "_score * foo bar + doc['myParent.weight'].value"} }
|
||||||
|
|
|
@ -19,15 +19,15 @@
|
||||||
|
|
||||||
package org.elasticsearch.example.painlesswhitelist;
|
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.PainlessExtension;
|
||||||
import org.elasticsearch.painless.spi.Whitelist;
|
import org.elasticsearch.painless.spi.Whitelist;
|
||||||
import org.elasticsearch.painless.spi.WhitelistLoader;
|
import org.elasticsearch.painless.spi.WhitelistLoader;
|
||||||
|
import org.elasticsearch.script.FieldScript;
|
||||||
import org.elasticsearch.script.ScriptContext;
|
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. */
|
/** An extension of painless which adds a whitelist. */
|
||||||
public class ExampleWhitelistExtension implements PainlessExtension {
|
public class ExampleWhitelistExtension implements PainlessExtension {
|
||||||
|
@ -37,6 +37,6 @@ public class ExampleWhitelistExtension implements PainlessExtension {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<ScriptContext<?>, List<Whitelist>> getContextWhitelists() {
|
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;
|
package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import org.elasticsearch.index.IndexSettings;
|
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.builder.SearchSourceBuilder;
|
||||||
import org.elasticsearch.search.fetch.subphase.DocValueFieldsContext;
|
import org.elasticsearch.search.fetch.subphase.DocValueFieldsContext;
|
||||||
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
|
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
|
||||||
|
@ -88,10 +88,10 @@ public abstract class InnerHitContextBuilder {
|
||||||
if (innerHitBuilder.getScriptFields() != null) {
|
if (innerHitBuilder.getScriptFields() != null) {
|
||||||
for (SearchSourceBuilder.ScriptField field : innerHitBuilder.getScriptFields()) {
|
for (SearchSourceBuilder.ScriptField field : innerHitBuilder.getScriptFields()) {
|
||||||
QueryShardContext innerContext = innerHitsContext.getQueryShardContext();
|
QueryShardContext innerContext = innerHitsContext.getQueryShardContext();
|
||||||
SearchScript.Factory factory = innerContext.getScriptService().compile(field.script(), SearchScript.CONTEXT);
|
FieldScript.Factory factory = innerContext.getScriptService().compile(field.script(), FieldScript.CONTEXT);
|
||||||
SearchScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), innerHitsContext.lookup());
|
FieldScript.LeafFactory fieldScript = factory.newFactory(field.script().getParams(), innerHitsContext.lookup());
|
||||||
innerHitsContext.scriptFields().add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
|
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) {
|
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;
|
public static final Map<String, ScriptContext<?>> CORE_CONTEXTS;
|
||||||
static {
|
static {
|
||||||
CORE_CONTEXTS = Stream.of(
|
CORE_CONTEXTS = Stream.of(
|
||||||
SearchScript.CONTEXT,
|
FieldScript.CONTEXT,
|
||||||
AggregationScript.CONTEXT,
|
AggregationScript.CONTEXT,
|
||||||
ScoreScript.CONTEXT,
|
ScoreScript.CONTEXT,
|
||||||
NumberSortScript.CONTEXT,
|
NumberSortScript.CONTEXT,
|
||||||
|
|
|
@ -58,8 +58,8 @@ import org.elasticsearch.index.shard.SearchOperationListener;
|
||||||
import org.elasticsearch.indices.IndicesService;
|
import org.elasticsearch.indices.IndicesService;
|
||||||
import org.elasticsearch.indices.cluster.IndicesClusterStateService.AllocatedIndices.IndexRemovalReason;
|
import org.elasticsearch.indices.cluster.IndicesClusterStateService.AllocatedIndices.IndexRemovalReason;
|
||||||
import org.elasticsearch.node.ResponseCollectorService;
|
import org.elasticsearch.node.ResponseCollectorService;
|
||||||
|
import org.elasticsearch.script.FieldScript;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.script.SearchScript;
|
|
||||||
import org.elasticsearch.search.aggregations.AggregationInitializationException;
|
import org.elasticsearch.search.aggregations.AggregationInitializationException;
|
||||||
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
import org.elasticsearch.search.aggregations.AggregatorFactories;
|
||||||
import org.elasticsearch.search.aggregations.InternalAggregation;
|
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.");
|
+ IndexSettings.MAX_SCRIPT_FIELDS_SETTING.getKey() + "] index level setting.");
|
||||||
}
|
}
|
||||||
for (org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField field : source.scriptFields()) {
|
for (org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField field : source.scriptFields()) {
|
||||||
SearchScript.Factory factory = scriptService.compile(field.script(), SearchScript.CONTEXT);
|
FieldScript.Factory factory = scriptService.compile(field.script(), FieldScript.CONTEXT);
|
||||||
SearchScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), context.lookup());
|
FieldScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), context.lookup());
|
||||||
context.scriptFields().add(new ScriptField(field.fieldName(), searchScript, field.ignoreFailure()));
|
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.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.IndexSettings;
|
import org.elasticsearch.index.IndexSettings;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
|
import org.elasticsearch.script.FieldScript;
|
||||||
import org.elasticsearch.script.Script;
|
import org.elasticsearch.script.Script;
|
||||||
import org.elasticsearch.script.SearchScript;
|
|
||||||
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||||
import org.elasticsearch.search.aggregations.AggregationBuilder;
|
import org.elasticsearch.search.aggregations.AggregationBuilder;
|
||||||
import org.elasticsearch.search.aggregations.AggregationInitializationException;
|
import org.elasticsearch.search.aggregations.AggregationInitializationException;
|
||||||
|
@ -566,8 +566,8 @@ public class TopHitsAggregationBuilder extends AbstractAggregationBuilder<TopHit
|
||||||
if (scriptFields != null) {
|
if (scriptFields != null) {
|
||||||
for (ScriptField field : scriptFields) {
|
for (ScriptField field : scriptFields) {
|
||||||
QueryShardContext shardContext = context.getQueryShardContext();
|
QueryShardContext shardContext = context.getQueryShardContext();
|
||||||
SearchScript.Factory factory = shardContext.getScriptService().compile(field.script(), SearchScript.CONTEXT);
|
FieldScript.Factory factory = shardContext.getScriptService().compile(field.script(), FieldScript.CONTEXT);
|
||||||
SearchScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), shardContext.lookup());
|
FieldScript.LeafFactory searchScript = factory.newFactory(field.script().getParams(), shardContext.lookup());
|
||||||
fields.add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
|
fields.add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
|
||||||
field.fieldName(), searchScript, field.ignoreFailure()));
|
field.fieldName(), searchScript, field.ignoreFailure()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.search.fetch.subphase;
|
package org.elasticsearch.search.fetch.subphase;
|
||||||
|
|
||||||
import org.elasticsearch.script.SearchScript;
|
import org.elasticsearch.script.FieldScript;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,10 +28,10 @@ public class ScriptFieldsContext {
|
||||||
|
|
||||||
public static class ScriptField {
|
public static class ScriptField {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final SearchScript.LeafFactory script;
|
private final FieldScript.LeafFactory script;
|
||||||
private final boolean ignoreException;
|
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.name = name;
|
||||||
this.script = script;
|
this.script = script;
|
||||||
this.ignoreException = ignoreException;
|
this.ignoreException = ignoreException;
|
||||||
|
@ -41,7 +41,7 @@ public class ScriptFieldsContext {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchScript.LeafFactory script() {
|
public FieldScript.LeafFactory script() {
|
||||||
return this.script;
|
return this.script;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.index.ReaderUtil;
|
import org.apache.lucene.index.ReaderUtil;
|
||||||
import org.elasticsearch.common.document.DocumentField;
|
import org.elasticsearch.common.document.DocumentField;
|
||||||
import org.elasticsearch.common.util.CollectionUtils;
|
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.SearchHit;
|
||||||
import org.elasticsearch.search.fetch.FetchSubPhase;
|
import org.elasticsearch.search.fetch.FetchSubPhase;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
|
@ -49,7 +49,7 @@ public final class ScriptFieldsFetchSubPhase implements FetchSubPhase {
|
||||||
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
|
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
|
||||||
|
|
||||||
int lastReaderId = -1;
|
int lastReaderId = -1;
|
||||||
SearchScript[] leafScripts = null;
|
FieldScript[] leafScripts = null;
|
||||||
List<ScriptFieldsContext.ScriptField> scriptFields = context.scriptFields().fields();
|
List<ScriptFieldsContext.ScriptField> scriptFields = context.scriptFields().fields();
|
||||||
final IndexReader reader = context.searcher().getIndexReader();
|
final IndexReader reader = context.searcher().getIndexReader();
|
||||||
for (SearchHit hit : hits) {
|
for (SearchHit hit : hits) {
|
||||||
|
@ -64,7 +64,7 @@ public final class ScriptFieldsFetchSubPhase implements FetchSubPhase {
|
||||||
leafScripts[i].setDocument(docId);
|
leafScripts[i].setDocument(docId);
|
||||||
final Object value;
|
final Object value;
|
||||||
try {
|
try {
|
||||||
value = leafScripts[i].run();
|
value = leafScripts[i].execute();
|
||||||
CollectionUtils.ensureNoSelfReferences(value, "ScriptFieldsFetchSubPhase leaf script " + i);
|
CollectionUtils.ensureNoSelfReferences(value, "ScriptFieldsFetchSubPhase leaf script " + i);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
if (scriptFields.get(i).ignoreException()) {
|
if (scriptFields.get(i).ignoreException()) {
|
||||||
|
@ -91,9 +91,9 @@ public final class ScriptFieldsFetchSubPhase implements FetchSubPhase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SearchScript[] createLeafScripts(LeafReaderContext context,
|
private FieldScript[] createLeafScripts(LeafReaderContext context,
|
||||||
List<ScriptFieldsContext.ScriptField> scriptFields) {
|
List<ScriptFieldsContext.ScriptField> scriptFields) {
|
||||||
SearchScript[] scripts = new SearchScript[scriptFields.size()];
|
FieldScript[] scripts = new FieldScript[scriptFields.size()];
|
||||||
for (int i = 0; i < scripts.length; i++) {
|
for (int i = 0; i < scripts.length; i++) {
|
||||||
try {
|
try {
|
||||||
scripts[i] = scriptFields.get(i).script().newInstance(context);
|
scripts[i] = scriptFields.get(i).script().newInstance(context);
|
||||||
|
|
|
@ -150,22 +150,22 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
public void testInlineScriptCompiledOnceCache() throws IOException {
|
public void testInlineScriptCompiledOnceCache() throws IOException {
|
||||||
buildScriptService(Settings.EMPTY);
|
buildScriptService(Settings.EMPTY);
|
||||||
Script script = new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap());
|
Script script = new Script(ScriptType.INLINE, "test", "1+1", Collections.emptyMap());
|
||||||
SearchScript.Factory factoryScript1 = scriptService.compile(script, SearchScript.CONTEXT);
|
FieldScript.Factory factoryScript1 = scriptService.compile(script, FieldScript.CONTEXT);
|
||||||
SearchScript.Factory factoryScript2 = scriptService.compile(script, SearchScript.CONTEXT);
|
FieldScript.Factory factoryScript2 = scriptService.compile(script, FieldScript.CONTEXT);
|
||||||
assertThat(factoryScript1, sameInstance(factoryScript2));
|
assertThat(factoryScript1, sameInstance(factoryScript2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllowAllScriptTypeSettings() throws IOException {
|
public void testAllowAllScriptTypeSettings() throws IOException {
|
||||||
buildScriptService(Settings.EMPTY);
|
buildScriptService(Settings.EMPTY);
|
||||||
|
|
||||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
assertCompileAccepted("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||||
assertCompileAccepted(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
assertCompileAccepted(null, "script", ScriptType.STORED, FieldScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllowAllScriptContextSettings() throws IOException {
|
public void testAllowAllScriptContextSettings() throws IOException {
|
||||||
buildScriptService(Settings.EMPTY);
|
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, AggregationScript.CONTEXT);
|
||||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, UpdateScript.CONTEXT);
|
assertCompileAccepted("painless", "script", ScriptType.INLINE, UpdateScript.CONTEXT);
|
||||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, IngestScript.CONTEXT);
|
assertCompileAccepted("painless", "script", ScriptType.INLINE, IngestScript.CONTEXT);
|
||||||
|
@ -176,16 +176,16 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
builder.put("script.allowed_types", "inline");
|
builder.put("script.allowed_types", "inline");
|
||||||
buildScriptService(builder.build());
|
buildScriptService(builder.build());
|
||||||
|
|
||||||
assertCompileAccepted("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
assertCompileAccepted("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||||
assertCompileRejected(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
assertCompileRejected(null, "script", ScriptType.STORED, FieldScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllowSomeScriptContextSettings() throws IOException {
|
public void testAllowSomeScriptContextSettings() throws IOException {
|
||||||
Settings.Builder builder = Settings.builder();
|
Settings.Builder builder = Settings.builder();
|
||||||
builder.put("script.allowed_contexts", "search, aggs");
|
builder.put("script.allowed_contexts", "field, aggs");
|
||||||
buildScriptService(builder.build());
|
buildScriptService(builder.build());
|
||||||
|
|
||||||
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, AggregationScript.CONTEXT);
|
||||||
assertCompileRejected("painless", "script", ScriptType.INLINE, UpdateScript.CONTEXT);
|
assertCompileRejected("painless", "script", ScriptType.INLINE, UpdateScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
@ -195,8 +195,8 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
builder.put("script.allowed_types", "none");
|
builder.put("script.allowed_types", "none");
|
||||||
buildScriptService(builder.build());
|
buildScriptService(builder.build());
|
||||||
|
|
||||||
assertCompileRejected("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
assertCompileRejected("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||||
assertCompileRejected(null, "script", ScriptType.STORED, SearchScript.CONTEXT);
|
assertCompileRejected(null, "script", ScriptType.STORED, FieldScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllowNoScriptContextSettings() throws IOException {
|
public void testAllowNoScriptContextSettings() throws IOException {
|
||||||
|
@ -204,7 +204,7 @@ public class ScriptServiceTests extends ESTestCase {
|
||||||
builder.put("script.allowed_contexts", "none");
|
builder.put("script.allowed_contexts", "none");
|
||||||
buildScriptService(builder.build());
|
buildScriptService(builder.build());
|
||||||
|
|
||||||
assertCompileRejected("painless", "script", ScriptType.INLINE, SearchScript.CONTEXT);
|
assertCompileRejected("painless", "script", ScriptType.INLINE, FieldScript.CONTEXT);
|
||||||
assertCompileRejected("painless", "script", ScriptType.INLINE, AggregationScript.CONTEXT);
|
assertCompileRejected("painless", "script", ScriptType.INLINE, AggregationScript.CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,8 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||||
scripts.put("doc['num1'].value * factor", vars -> {
|
scripts.put("doc['num1'].value * factor", vars -> {
|
||||||
Map<?, ?> doc = (Map) vars.get("doc");
|
Map<?, ?> doc = (Map) vars.get("doc");
|
||||||
ScriptDocValues.Doubles num1 = (ScriptDocValues.Doubles) doc.get("num1");
|
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;
|
return num1.getValue() * factor;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,17 @@ public class MockScriptEngine implements ScriptEngine {
|
||||||
if (context.instanceClazz.equals(SearchScript.class)) {
|
if (context.instanceClazz.equals(SearchScript.class)) {
|
||||||
SearchScript.Factory factory = mockCompiled::createSearchScript;
|
SearchScript.Factory factory = mockCompiled::createSearchScript;
|
||||||
return context.factoryClazz.cast(factory);
|
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)) {
|
} else if(context.instanceClazz.equals(TermsSetQueryScript.class)) {
|
||||||
TermsSetQueryScript.Factory factory = (parameters, lookup) -> (TermsSetQueryScript.LeafFactory) ctx
|
TermsSetQueryScript.Factory factory = (parameters, lookup) -> (TermsSetQueryScript.LeafFactory) ctx
|
||||||
-> new TermsSetQueryScript(parameters, lookup, 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 + "]");
|
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 {
|
public class MockCompiledScript {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
|
@ -10,15 +10,15 @@ import org.elasticsearch.painless.spi.Whitelist;
|
||||||
import org.elasticsearch.painless.spi.WhitelistLoader;
|
import org.elasticsearch.painless.spi.WhitelistLoader;
|
||||||
import org.elasticsearch.script.AggregationScript;
|
import org.elasticsearch.script.AggregationScript;
|
||||||
import org.elasticsearch.script.BucketAggregationSelectorScript;
|
import org.elasticsearch.script.BucketAggregationSelectorScript;
|
||||||
|
import org.elasticsearch.script.FieldScript;
|
||||||
import org.elasticsearch.script.FilterScript;
|
import org.elasticsearch.script.FilterScript;
|
||||||
|
import org.elasticsearch.script.NumberSortScript;
|
||||||
import org.elasticsearch.script.ScriptContext;
|
import org.elasticsearch.script.ScriptContext;
|
||||||
import org.elasticsearch.script.SearchScript;
|
import org.elasticsearch.script.StringSortScript;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.elasticsearch.script.NumberSortScript;
|
|
||||||
import org.elasticsearch.script.StringSortScript;
|
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ public class SqlPainlessExtension implements PainlessExtension {
|
||||||
List<Whitelist> list = singletonList(WHITELIST);
|
List<Whitelist> list = singletonList(WHITELIST);
|
||||||
whitelist.put(FilterScript.CONTEXT, list);
|
whitelist.put(FilterScript.CONTEXT, list);
|
||||||
whitelist.put(AggregationScript.CONTEXT, list);
|
whitelist.put(AggregationScript.CONTEXT, list);
|
||||||
whitelist.put(SearchScript.CONTEXT, list);
|
whitelist.put(FieldScript.CONTEXT, list);
|
||||||
whitelist.put(NumberSortScript.CONTEXT, list);
|
whitelist.put(NumberSortScript.CONTEXT, list);
|
||||||
whitelist.put(StringSortScript.CONTEXT, list);
|
whitelist.put(StringSortScript.CONTEXT, list);
|
||||||
whitelist.put(BucketAggregationSelectorScript.CONTEXT, list);
|
whitelist.put(BucketAggregationSelectorScript.CONTEXT, list);
|
||||||
|
|
Loading…
Reference in New Issue