/*
* 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.mustache;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.test.ESSingleNodeTestCase;
import org.junit.Before;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
/**
* Full integration test of the template query plugin.
*/
public class SearchTemplateIT extends ESSingleNodeTestCase {
@Override
protected Collection<Class<? extends Plugin>> getPlugins() {
return Collections.singleton(MustachePlugin.class);
}
@Before
public void setup() throws IOException {
createIndex("test");
client().prepareIndex("test", "type", "1")
.setSource(jsonBuilder().startObject().field("text", "value1").endObject())
.get();
client().prepareIndex("test", "type", "2")
.setSource(jsonBuilder().startObject().field("text", "value2").endObject())
.get();
client().admin().indices().prepareRefresh().get();
}
// Relates to #6318
public void testSearchRequestFail() throws Exception {
String query = "{ \"query\": {\"match_all\": {}}, \"size\" : \"{{my_size}}\" }";
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("_all");
expectThrows(Exception.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(searchRequest)
.setScript(query)
.setScriptType(ScriptType.INLINE)
.setScriptParams(randomBoolean() ? null : Collections.emptyMap())
.get());
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(searchRequest)
.setScript(query)
.setScriptType(ScriptType.INLINE)
.setScriptParams(Collections.singletonMap("my_size", 1))
.get();
assertThat(searchResponse.getResponse().getHits().getHits().length, equalTo(1));
}
/**
* Test that template can be expressed as a single escaped string.
*/
public void testTemplateQueryAsEscapedString() throws Exception {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("_all");
String query =
"{" + " \"inline\" : \"{ \\\"size\\\": \\\"{{size}}\\\", \\\"query\\\":{\\\"match_all\\\":{}}}\","
+ " \"params\":{"
+ " \"size\": 1"
+ " }"
+ "}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(createParser(JsonXContent.jsonXContent, query));
request.setRequest(searchRequest);
SearchTemplateResponse searchResponse = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertThat(searchResponse.getResponse().getHits().getHits().length, equalTo(1));
}
/**
* Test that template can contain conditional clause. In this case it is at
* the beginning of the string.
*/
public void testTemplateQueryAsEscapedStringStartingWithConditionalClause() throws Exception {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("_all");
String templateString =
"{"
+ " \"inline\" : \"{ {{#use_size}} \\\"size\\\": \\\"{{size}}\\\", {{/use_size}} \\\"query\\\":{\\\"match_all\\\":{}}}\","
+ " \"params\":{"
+ " \"size\": 1,"
+ " \"use_size\": true"
+ " }"
+ "}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(createParser(JsonXContent.jsonXContent, templateString));
request.setRequest(searchRequest);
SearchTemplateResponse searchResponse = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertThat(searchResponse.getResponse().getHits().getHits().length, equalTo(1));
}
/**
* Test that template can contain conditional clause. In this case it is at
* the end of the string.
*/
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"
+ " }"
+ "}";
SearchTemplateRequest request = RestSearchTemplateAction.parse(createParser(JsonXContent.jsonXContent, templateString));
request.setRequest(searchRequest);
SearchTemplateResponse searchResponse = client().execute(SearchTemplateAction.INSTANCE, request).get();
assertThat(searchResponse.getResponse().getHits().getHits().length, equalTo(1));
}
public void testIndexedTemplateClient() throws Exception {
assertAcked(client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("testTemplate")
.setContent(new BytesArray("{" +
"\"template\":{" +
" \"query\":{" +
" \"match\":{" +
" \"theField\" : \"{{fieldParam}}\"}" +
" }" +
"}" +
"}"), XContentType.JSON));
assertAcked(client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("testTemplate").setContent(new BytesArray("{" +
"\"template\":{" +
" \"query\":{" +
" \"match\":{" +
" \"theField\" : \"{{fieldParam}}\"}" +
" }" +
"}" +
"}"), XContentType.JSON));
GetStoredScriptResponse getResponse = client().admin().cluster()
.prepareGetStoredScript(MustacheScriptEngine.NAME, "testTemplate").get();
assertNotNull(getResponse.getSource());
BulkRequestBuilder bulkRequestBuilder = client().prepareBulk();
bulkRequestBuilder.add(client().prepareIndex("test", "type", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "2").setSource("{\"theField\":\"foo 2\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "3").setSource("{\"theField\":\"foo 3\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "4").setSource("{\"theField\":\"foo 4\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "5").setSource("{\"theField\":\"bar\"}", XContentType.JSON));
bulkRequestBuilder.get();
client().admin().indices().prepareRefresh().get();
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("fieldParam", "foo");
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("testTemplate").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get();
assertHitCount(searchResponse.getResponse(), 4);
assertAcked(client().admin().cluster()
.prepareDeleteStoredScript(MustacheScriptEngine.NAME, "testTemplate"));
getResponse = client().admin().cluster()
.prepareGetStoredScript(MustacheScriptEngine.NAME, "testTemplate").get();
assertNull(getResponse.getSource());
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("/template_index/mustache/1000").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get());
assertThat(e.getMessage(), containsString("illegal stored script format [/template_index/mustache/1000] use only <id>"));
}
public void testIndexedTemplate() throws Exception {
assertAcked(client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("1a")
.setContent(new BytesArray("{" +
"\"template\":{" +
" \"query\":{" +
" \"match\":{" +
" \"theField\" : \"{{fieldParam}}\"}" +
" }" +
"}" +
"}"
), XContentType.JSON)
);
assertAcked(client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("2")
.setContent(new BytesArray("{" +
"\"template\":{" +
" \"query\":{" +
" \"match\":{" +
" \"theField\" : \"{{fieldParam}}\"}" +
" }" +
"}" +
"}"), XContentType.JSON)
);
assertAcked(client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("3")
.setContent(new BytesArray("{" +
"\"template\":{" +
" \"match\":{" +
" \"theField\" : \"{{fieldParam}}\"}" +
" }" +
"}"), XContentType.JSON)
);
BulkRequestBuilder bulkRequestBuilder = client().prepareBulk();
bulkRequestBuilder.add(client().prepareIndex("test", "type", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "2").setSource("{\"theField\":\"foo 2\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "3").setSource("{\"theField\":\"foo 3\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "4").setSource("{\"theField\":\"foo 4\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "5").setSource("{\"theField\":\"bar\"}", XContentType.JSON));
bulkRequestBuilder.get();
client().admin().indices().prepareRefresh().get();
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("fieldParam", "foo");
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest().indices("test").types("type"))
.setScript("1a")
.setScriptType(ScriptType.STORED)
.setScriptParams(templateParams)
.get();
assertHitCount(searchResponse.getResponse(), 4);
expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest().indices("test").types("type"))
.setScript("/template_index/mustache/1000")
.setScriptType(ScriptType.STORED)
.setScriptParams(templateParams)
.get());
expectThrows(IllegalArgumentException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest().indices("test").types("type"))
.setScript("/myindex/mustache/1")
.setScriptType(ScriptType.STORED)
.setScriptParams(templateParams)
.get());
templateParams.put("fieldParam", "bar");
searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("/mustache/2").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get();
assertHitCount(searchResponse.getResponse(), 1);
assertWarnings("use of </lang/id> [/mustache/2] for looking up" +
" stored scripts/templates has been deprecated, use only <id> [2] instead");
}
// Relates to #10397
public void testIndexedTemplateOverwrite() throws Exception {
createIndex("testindex");
ensureGreen("testindex");
client().prepareIndex("testindex", "test", "1")
.setSource(jsonBuilder().startObject().field("searchtext", "dev1").endObject())
.get();
client().admin().indices().prepareRefresh().get();
int iterations = randomIntBetween(2, 11);
for (int i = 1; i < iterations; i++) {
assertAcked(client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("git01")
.setContent(new BytesArray("{\"template\":{\"query\": {\"match\": {\"searchtext\": {\"query\": \"{{P_Keyword1}}\"," +
"\"type\": \"ooophrase_prefix\"}}}}}"), XContentType.JSON));
GetStoredScriptResponse getResponse = client().admin().cluster()
.prepareGetStoredScript(MustacheScriptEngine.NAME, "git01").get();
assertNotNull(getResponse.getSource());
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("P_Keyword1", "dev");
ParsingException e = expectThrows(ParsingException.class, () -> new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("testindex").types("test"))
.setScript("git01").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get());
assertThat(e.getMessage(), containsString("[match] query does not support type ooophrase_prefix"));
assertWarnings("Deprecated field [type] used, replaced by [match_phrase and match_phrase_prefix query]");
assertAcked(client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("git01")
.setContent(new BytesArray("{\"query\": {\"match\": {\"searchtext\": {\"query\": \"{{P_Keyword1}}\"," +
"\"type\": \"phrase_prefix\"}}}}"), XContentType.JSON));
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("testindex").types("test"))
.setScript("git01").setScriptType(ScriptType.STORED).setScriptParams(templateParams)
.get();
assertHitCount(searchResponse.getResponse(), 1);
assertWarnings("Deprecated field [type] used, replaced by [match_phrase and match_phrase_prefix query]");
}
}
public void testIndexedTemplateWithArray() throws Exception {
String multiQuery = "{\"query\":{\"terms\":{\"theField\":[\"{{#fieldParam}}\",\"{{.}}\",\"{{/fieldParam}}\"]}}}";
assertAcked(
client().admin().cluster().preparePutStoredScript()
.setLang(MustacheScriptEngine.NAME)
.setId("4")
.setContent(jsonBuilder().startObject().field("template", multiQuery).endObject().bytes(), XContentType.JSON)
);
BulkRequestBuilder bulkRequestBuilder = client().prepareBulk();
bulkRequestBuilder.add(client().prepareIndex("test", "type", "1").setSource("{\"theField\":\"foo\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "2").setSource("{\"theField\":\"foo 2\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "3").setSource("{\"theField\":\"foo 3\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "4").setSource("{\"theField\":\"foo 4\"}", XContentType.JSON));
bulkRequestBuilder.add(client().prepareIndex("test", "type", "5").setSource("{\"theField\":\"bar\"}", XContentType.JSON));
bulkRequestBuilder.get();
client().admin().indices().prepareRefresh().get();
Map<String, Object> arrayTemplateParams = new HashMap<>();
String[] fieldParams = {"foo", "bar"};
arrayTemplateParams.put("fieldParam", fieldParams);
SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client())
.setRequest(new SearchRequest("test").types("type"))
.setScript("4").setScriptType(ScriptType.STORED).setScriptParams(arrayTemplateParams)
.get();
assertHitCount(searchResponse.getResponse(), 5);
}
}