Skip to content

Commit 0003b8e

Browse files
committed
Add support for search templates to the high-level REST client.
1 parent 5bee4c3 commit 0003b8e

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
import org.elasticsearch.plugins.spi.NamedXContentProvider;
6565
import org.elasticsearch.rest.BytesRestResponse;
6666
import org.elasticsearch.rest.RestStatus;
67+
import org.elasticsearch.script.mustache.SearchTemplateRequest;
68+
import org.elasticsearch.script.mustache.SearchTemplateResponse;
6769
import org.elasticsearch.search.aggregations.Aggregation;
6870
import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder;
6971
import org.elasticsearch.search.aggregations.bucket.adjacency.ParsedAdjacencyMatrix;
@@ -501,6 +503,32 @@ public final void clearScrollAsync(ClearScrollRequest clearScrollRequest,
501503
listener, emptySet(), headers);
502504
}
503505

506+
/**
507+
* Executes a request using the Search Template API.
508+
*
509+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html">Search Template API
510+
* on elastic.co</a>.
511+
*/
512+
public final SearchTemplateResponse searchTemplate(SearchTemplateRequest searchTemplateRequest,
513+
Header... headers) throws IOException {
514+
return performRequestAndParseEntity(searchTemplateRequest, RequestConverters::searchTemplate,
515+
SearchTemplateResponse::fromXContent, emptySet(), headers);
516+
}
517+
518+
/**
519+
* Asynchronously executes a request using the Search Template API
520+
*
521+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html">Search Template API
522+
* on elastic.co</a>.
523+
*/
524+
public final void searchTemplateAsync(SearchTemplateRequest searchTemplateRequest,
525+
ActionListener<SearchTemplateResponse> listener,
526+
Header... headers) {
527+
performRequestAsyncAndParseEntity(searchTemplateRequest, RequestConverters::searchTemplate,
528+
SearchTemplateResponse::fromXContent, listener, emptySet(), headers);
529+
}
530+
531+
504532
/**
505533
* Executes a request using the Ranking Evaluation API.
506534
*

client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.java

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@
3838
import org.elasticsearch.action.search.SearchResponse;
3939
import org.elasticsearch.action.search.SearchScrollRequest;
4040
import org.elasticsearch.common.Strings;
41+
import org.elasticsearch.common.bytes.BytesReference;
4142
import org.elasticsearch.common.unit.TimeValue;
4243
import org.elasticsearch.common.xcontent.XContentBuilder;
44+
import org.elasticsearch.common.xcontent.XContentFactory;
45+
import org.elasticsearch.common.xcontent.XContentType;
4346
import org.elasticsearch.index.query.MatchQueryBuilder;
4447
import org.elasticsearch.index.query.ScriptQueryBuilder;
4548
import org.elasticsearch.index.query.TermsQueryBuilder;
@@ -48,6 +51,8 @@
4851
import org.elasticsearch.rest.RestStatus;
4952
import org.elasticsearch.script.Script;
5053
import org.elasticsearch.script.ScriptType;
54+
import org.elasticsearch.script.mustache.SearchTemplateRequest;
55+
import org.elasticsearch.script.mustache.SearchTemplateResponse;
5156
import org.elasticsearch.search.SearchHit;
5257
import org.elasticsearch.search.aggregations.BucketOrder;
5358
import org.elasticsearch.search.aggregations.bucket.range.Range;
@@ -69,10 +74,12 @@
6974
import java.io.IOException;
7075
import java.util.Arrays;
7176
import java.util.Collections;
77+
import java.util.HashMap;
7278
import java.util.List;
7379
import java.util.Map;
7480

7581
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
82+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
7683
import static org.hamcrest.Matchers.both;
7784
import static org.hamcrest.Matchers.containsString;
7885
import static org.hamcrest.Matchers.either;
@@ -733,6 +740,103 @@ public void testMultiSearch_failure() throws Exception {
733740
assertThat(multiSearchResponse.getResponses()[1].getResponse(), nullValue());
734741
}
735742

743+
public void testSearchTemplate() throws IOException {
744+
SearchTemplateRequest searchTemplateRequest = new SearchTemplateRequest();
745+
searchTemplateRequest.setRequest(new SearchRequest("index"));
746+
747+
searchTemplateRequest.setScriptType(ScriptType.INLINE);
748+
searchTemplateRequest.setScript(
749+
"{" +
750+
" \"query\": {" +
751+
" \"match\": {" +
752+
" \"num\": {{number}}" +
753+
" }" +
754+
" }" +
755+
"}");
756+
757+
Map<String, Object> scriptParams = new HashMap<>();
758+
scriptParams.put("number", 10);
759+
searchTemplateRequest.setScriptParams(scriptParams);
760+
761+
searchTemplateRequest.setExplain(true);
762+
searchTemplateRequest.setProfile(true);
763+
764+
SearchTemplateResponse searchTemplateResponse = execute(searchTemplateRequest,
765+
highLevelClient()::searchTemplate,
766+
highLevelClient()::searchTemplateAsync);
767+
768+
assertNull(searchTemplateResponse.getSource());
769+
770+
SearchResponse searchResponse = searchTemplateResponse.getResponse();
771+
assertNotNull(searchResponse);
772+
773+
assertEquals(1, searchResponse.getHits().totalHits);
774+
assertEquals(1, searchResponse.getHits().getHits().length);
775+
assertThat(searchResponse.getHits().getMaxScore(), greaterThan(0f));
776+
777+
SearchHit hit = searchResponse.getHits().getHits()[0];
778+
assertNotNull(hit.getExplanation());
779+
780+
assertFalse(searchResponse.getProfileResults().isEmpty());
781+
}
782+
783+
public void testNonExistentSearchTemplate() {
784+
SearchTemplateRequest searchTemplateRequest = new SearchTemplateRequest();
785+
searchTemplateRequest.setRequest(new SearchRequest("index"));
786+
787+
searchTemplateRequest.setScriptType(ScriptType.STORED);
788+
searchTemplateRequest.setScript("non-existent");
789+
searchTemplateRequest.setScriptParams(Collections.emptyMap());
790+
791+
ElasticsearchStatusException exception = expectThrows(ElasticsearchStatusException.class,
792+
() -> execute(searchTemplateRequest,
793+
highLevelClient()::searchTemplate,
794+
highLevelClient()::searchTemplateAsync));
795+
796+
assertEquals(RestStatus.NOT_FOUND, exception.status());
797+
}
798+
799+
public void testRenderSearchTemplate() throws IOException {
800+
SearchTemplateRequest searchTemplateRequest = new SearchTemplateRequest();
801+
802+
searchTemplateRequest.setScriptType(ScriptType.INLINE);
803+
searchTemplateRequest.setScript(
804+
"{" +
805+
" \"query\": {" +
806+
" \"match\": {" +
807+
" \"num\": {{number}}" +
808+
" }" +
809+
" }" +
810+
"}");
811+
812+
Map<String, Object> scriptParams = new HashMap<>();
813+
scriptParams.put("number", 10);
814+
searchTemplateRequest.setScriptParams(scriptParams);
815+
816+
// Setting simulate true causes the template to only be rendered.
817+
searchTemplateRequest.setSimulate(true);
818+
819+
SearchTemplateResponse searchTemplateResponse = execute(searchTemplateRequest,
820+
highLevelClient()::searchTemplate,
821+
highLevelClient()::searchTemplateAsync);
822+
assertNull(searchTemplateResponse.getResponse());
823+
824+
BytesReference expectedSource = BytesReference.bytes(
825+
XContentFactory.jsonBuilder()
826+
.startObject()
827+
.startObject("query")
828+
.startObject("match")
829+
.field("num", 10)
830+
.endObject()
831+
.endObject()
832+
.endObject());
833+
834+
BytesReference actualSource = searchTemplateResponse.getSource();
835+
assertNotNull(actualSource);
836+
837+
assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON);
838+
}
839+
736840
public void testFieldCaps() throws IOException {
737841
FieldCapabilitiesRequest request = new FieldCapabilitiesRequest()
738842
.indices("index1", "index2")

0 commit comments

Comments
 (0)