Scripting: Execute Scripting Engine before searching for inner templates in template query
The search template and template query did not run the template through the script engine before searching for an inner template. This meant that parsing for the inner template failed because the template was not always valid JSON (if it contained mustache code) when it was parsed to find the inner template. This has been fixed and Tests added to check for the failing behaviour. Tests are from https://github.com/elastic/elasticsearch/pull/8393
This commit is contained in:
parent
e359698605
commit
f336cea35e
|
@ -22,7 +22,6 @@ package org.elasticsearch.search;
|
|||
import com.carrotsearch.hppc.ObjectHashSet;
|
||||
import com.carrotsearch.hppc.ObjectSet;
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
|
@ -682,9 +681,10 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
|
|||
|
||||
private void parseTemplate(ShardSearchRequest request) {
|
||||
|
||||
final ExecutableScript executable;
|
||||
BytesReference processedQuery;
|
||||
if (request.template() != null) {
|
||||
executable = this.scriptService.executable(request.template(), ScriptContext.Standard.SEARCH);
|
||||
ExecutableScript executable = this.scriptService.executable(request.template(), ScriptContext.Standard.SEARCH);
|
||||
processedQuery = (BytesReference) executable.run();
|
||||
} else {
|
||||
if (!hasLength(request.templateSource())) {
|
||||
return;
|
||||
|
@ -700,13 +700,16 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
|
|||
//Try to double parse for nested template id/file
|
||||
parser = null;
|
||||
try {
|
||||
byte[] templateBytes = template.getScript().getBytes(Charsets.UTF_8);
|
||||
parser = XContentFactory.xContent(templateBytes).createParser(templateBytes);
|
||||
ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH);
|
||||
processedQuery = (BytesReference) executable.run();
|
||||
parser = XContentFactory.xContent(processedQuery).createParser(processedQuery);
|
||||
} catch (ElasticsearchParseException epe) {
|
||||
//This was an non-nested template, the parse failure was due to this, it is safe to assume this refers to a file
|
||||
//for backwards compatibility and keep going
|
||||
template = new Template(template.getScript(), ScriptService.ScriptType.FILE, MustacheScriptEngineService.NAME,
|
||||
null, template.getParams());
|
||||
ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH);
|
||||
processedQuery = (BytesReference) executable.run();
|
||||
}
|
||||
if (parser != null) {
|
||||
try {
|
||||
|
@ -715,11 +718,16 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
|
|||
//An inner template referring to a filename or id
|
||||
template = new Template(innerTemplate.getScript(), innerTemplate.getType(),
|
||||
MustacheScriptEngineService.NAME, null, template.getParams());
|
||||
ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH);
|
||||
processedQuery = (BytesReference) executable.run();
|
||||
}
|
||||
} catch (ScriptParseException e) {
|
||||
// No inner template found, use original template from above
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH);
|
||||
processedQuery = (BytesReference) executable.run();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchParseException("Failed to parse template", e);
|
||||
|
@ -730,10 +738,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
|
|||
if (!hasLength(template.getScript())) {
|
||||
throw new ElasticsearchParseException("Template must have [template] field configured");
|
||||
}
|
||||
executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH);
|
||||
}
|
||||
|
||||
BytesReference processedQuery = (BytesReference) executable.run();
|
||||
request.source(processedQuery);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,35 @@ public class TemplateQueryParserTest extends ElasticsearchTestCase {
|
|||
assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTemplateAsSingleStringWithConditionalClause() throws IOException {
|
||||
String templateString = "{" + " \"inline\" : \"{ \\\"match_{{#use_it}}{{template}}{{/use_it}}\\\":{} }\"," + " \"params\":{"
|
||||
+ " \"template\":\"all\"," + " \"use_it\": true" + " }" + "}";
|
||||
XContentParser templateSourceParser = XContentFactory.xContent(templateString).createParser(templateString);
|
||||
context.reset(templateSourceParser);
|
||||
|
||||
TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
|
||||
Query query = parser.parse(context);
|
||||
assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the template query parser can parse and evaluate template
|
||||
* expressed as a single string but still it expects only the query
|
||||
* specification (thus this test should fail with specific exception).
|
||||
*/
|
||||
@Test(expected = QueryParsingException.class)
|
||||
public void testParseTemplateFailsToParseCompleteQueryAsSingleString() throws IOException {
|
||||
String templateString = "{" + " \"inline\" : \"{ \\\"size\\\": \\\"{{size}}\\\", \\\"query\\\":{\\\"match_all\\\":{}}}\","
|
||||
+ " \"params\":{" + " \"size\":2" + " }\n" + "}";
|
||||
|
||||
XContentParser templateSourceParser = XContentFactory.xContent(templateString).createParser(templateString);
|
||||
context.reset(templateSourceParser);
|
||||
|
||||
TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
|
||||
parser.parse(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParserCanExtractTemplateNames() throws Exception {
|
||||
String templateString = "{ \"file\": \"storedTemplate\" ,\"params\":{\"template\":\"all\" } } ";
|
||||
|
|
|
@ -232,6 +232,67 @@ public class TemplateQueryTest extends ElasticsearchIntegrationTest {
|
|||
assertHitCount(searchResponse, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTemplateQueryFromFile() throws Exception {
|
||||
SearchRequest searchRequest = new SearchRequest();
|
||||
searchRequest.indices("_all");
|
||||
String templateString = "{" + " \"file\": \"full-query-template\"," + " \"params\":{" + " \"mySize\": 2,"
|
||||
+ " \"myField\": \"text\"," + " \"myValue\": \"value1\"" + " }" + "}";
|
||||
BytesReference bytesRef = new BytesArray(templateString);
|
||||
searchRequest.templateSource(bytesRef);
|
||||
SearchResponse searchResponse = client().search(searchRequest).get();
|
||||
assertThat(searchResponse.getHits().hits().length, equalTo(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that template can be expressed as a single escaped string.
|
||||
*/
|
||||
@Test
|
||||
public void testTemplateQueryAsEscapedString() throws Exception {
|
||||
SearchRequest searchRequest = new SearchRequest();
|
||||
searchRequest.indices("_all");
|
||||
String templateString = "{" + " \"template\" : \"{ \\\"size\\\": \\\"{{size}}\\\", \\\"query\\\":{\\\"match_all\\\":{}}}\","
|
||||
+ " \"params\":{" + " \"size\": 1" + " }" + "}";
|
||||
BytesReference bytesRef = new BytesArray(templateString);
|
||||
searchRequest.templateSource(bytesRef);
|
||||
SearchResponse searchResponse = client().search(searchRequest).get();
|
||||
assertThat(searchResponse.getHits().hits().length, equalTo(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that template can contain conditional clause. In this case it is at
|
||||
* the beginning of the string.
|
||||
*/
|
||||
@Test
|
||||
public void testTemplateQueryAsEscapedStringStartingWithConditionalClause() throws Exception {
|
||||
SearchRequest searchRequest = new SearchRequest();
|
||||
searchRequest.indices("_all");
|
||||
String templateString = "{"
|
||||
+ " \"template\" : \"{ {{#use_size}} \\\"size\\\": \\\"{{size}}\\\", {{/use_size}} \\\"query\\\":{\\\"match_all\\\":{}}}\","
|
||||
+ " \"params\":{" + " \"size\": 1," + " \"use_size\": true" + " }" + "}";
|
||||
BytesReference bytesRef = new BytesArray(templateString);
|
||||
searchRequest.templateSource(bytesRef);
|
||||
SearchResponse searchResponse = client().search(searchRequest).get();
|
||||
assertThat(searchResponse.getHits().hits().length, equalTo(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that template can contain conditional clause. In this case it is at
|
||||
* the end of the string.
|
||||
*/
|
||||
@Test
|
||||
public void testTemplateQueryAsEscapedStringWithConditionalClauseAtEnd() throws Exception {
|
||||
SearchRequest searchRequest = new SearchRequest();
|
||||
searchRequest.indices("_all");
|
||||
String templateString = "{"
|
||||
+ " \"inline\" : \"{ \\\"query\\\":{\\\"match_all\\\":{}} {{#use_size}}, \\\"size\\\": \\\"{{size}}\\\" {{/use_size}} }\","
|
||||
+ " \"params\":{" + " \"size\": 1," + " \"use_size\": true" + " }" + "}";
|
||||
BytesReference bytesRef = new BytesArray(templateString);
|
||||
searchRequest.templateSource(bytesRef);
|
||||
SearchResponse searchResponse = client().search(searchRequest).get();
|
||||
assertThat(searchResponse.getHits().hits().length, equalTo(1));
|
||||
}
|
||||
|
||||
@Test(expected = SearchPhaseExecutionException.class)
|
||||
public void testIndexedTemplateClient() throws Exception {
|
||||
createIndex(ScriptService.SCRIPT_INDEX);
|
||||
|
|
Loading…
Reference in New Issue