Skip to content

Commit fcdc401

Browse files
authored
Add MultiSearchTemplate support to High Level Rest client (#31662)
* Add MultiSearchTemplate support to High Level Rest client Backport of 30836 (minus the “took” param which is reliant on a backport of 30957 which in turn relies on 23767 which now can’t be cherry-picked to 6.x) * Fix broken references to “took”
1 parent 4d2de5b commit fcdc401

File tree

17 files changed

+808
-81
lines changed

17 files changed

+808
-81
lines changed

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

+16
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
import org.elasticsearch.index.VersionType;
102102
import org.elasticsearch.index.rankeval.RankEvalRequest;
103103
import org.elasticsearch.rest.action.search.RestSearchAction;
104+
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
104105
import org.elasticsearch.script.mustache.SearchTemplateRequest;
105106
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
106107
import org.elasticsearch.tasks.TaskId;
@@ -611,6 +612,21 @@ static Request searchTemplate(SearchTemplateRequest searchTemplateRequest) throw
611612
request.setEntity(createEntity(searchTemplateRequest, REQUEST_BODY_CONTENT_TYPE));
612613
return request;
613614
}
615+
616+
static Request multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplateRequest) throws IOException {
617+
Request request = new Request(HttpPost.METHOD_NAME, "/_msearch/template");
618+
619+
Params params = new Params(request);
620+
params.putParam(RestSearchAction.TYPED_KEYS_PARAM, "true");
621+
if (multiSearchTemplateRequest.maxConcurrentSearchRequests() != MultiSearchRequest.MAX_CONCURRENT_SEARCH_REQUESTS_DEFAULT) {
622+
params.putParam("max_concurrent_searches", Integer.toString(multiSearchTemplateRequest.maxConcurrentSearchRequests()));
623+
}
624+
625+
XContent xContent = REQUEST_BODY_CONTENT_TYPE.xContent();
626+
byte[] source = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, xContent);
627+
request.setEntity(new ByteArrayEntity(source, createContentType(xContent.type())));
628+
return request;
629+
}
614630

615631
static Request existsAlias(GetAliasesRequest getAliasesRequest) {
616632
if ((getAliasesRequest.indices() == null || getAliasesRequest.indices().length == 0) &&

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

+28
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
import org.elasticsearch.plugins.spi.NamedXContentProvider;
6969
import org.elasticsearch.rest.BytesRestResponse;
7070
import org.elasticsearch.rest.RestStatus;
71+
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
72+
import org.elasticsearch.script.mustache.MultiSearchTemplateResponse;
7173
import org.elasticsearch.script.mustache.SearchTemplateRequest;
7274
import org.elasticsearch.script.mustache.SearchTemplateResponse;
7375
import org.elasticsearch.search.aggregations.Aggregation;
@@ -977,6 +979,32 @@ public final void rankEvalAsync(RankEvalRequest rankEvalRequest, RequestOptions
977979
emptySet());
978980
}
979981

982+
983+
/**
984+
* Executes a request using the Multi Search Template API.
985+
*
986+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-search-template.html">Multi Search Template API
987+
* on elastic.co</a>.
988+
*/
989+
public final MultiSearchTemplateResponse multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplateRequest,
990+
RequestOptions options) throws IOException {
991+
return performRequestAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
992+
options, MultiSearchTemplateResponse::fromXContext, emptySet());
993+
}
994+
995+
/**
996+
* Asynchronously executes a request using the Multi Search Template API
997+
*
998+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-search-template.html">Multi Search Template API
999+
* on elastic.co</a>.
1000+
*/
1001+
public final void multiSearchTemplateAsync(MultiSearchTemplateRequest multiSearchTemplateRequest,
1002+
RequestOptions options,
1003+
ActionListener<MultiSearchTemplateResponse> listener) {
1004+
performRequestAsyncAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
1005+
options, MultiSearchTemplateResponse::fromXContext, listener, emptySet());
1006+
}
1007+
9801008
/**
9811009
* Asynchronously executes a request using the Ranking Evaluation API.
9821010
*

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

+49-2
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
import org.elasticsearch.repositories.fs.FsRepository;
123123
import org.elasticsearch.rest.action.search.RestSearchAction;
124124
import org.elasticsearch.script.ScriptType;
125+
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
125126
import org.elasticsearch.script.mustache.SearchTemplateRequest;
126127
import org.elasticsearch.search.Scroll;
127128
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
@@ -1400,7 +1401,53 @@ public void testRenderSearchTemplate() throws Exception {
14001401
assertEquals(Collections.emptyMap(), request.getParameters());
14011402
assertToXContentBody(searchTemplateRequest, request.getEntity());
14021403
}
1403-
1404+
1405+
public void testMultiSearchTemplate() throws Exception {
1406+
final int numSearchRequests = randomIntBetween(1, 10);
1407+
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
1408+
1409+
for (int i = 0; i < numSearchRequests; i++) {
1410+
// Create a random request.
1411+
String[] indices = randomIndicesNames(0, 5);
1412+
SearchRequest searchRequest = new SearchRequest(indices);
1413+
1414+
Map<String, String> expectedParams = new HashMap<>();
1415+
setRandomSearchParams(searchRequest, expectedParams);
1416+
1417+
// scroll is not supported in the current msearch or msearchtemplate api, so unset it:
1418+
searchRequest.scroll((Scroll) null);
1419+
// batched reduce size is currently not set-able on a per-request basis as it is a query string parameter only
1420+
searchRequest.setBatchedReduceSize(SearchRequest.DEFAULT_BATCHED_REDUCE_SIZE);
1421+
1422+
setRandomIndicesOptions(searchRequest::indicesOptions, searchRequest::indicesOptions, expectedParams);
1423+
1424+
SearchTemplateRequest searchTemplateRequest = new SearchTemplateRequest(searchRequest);
1425+
1426+
searchTemplateRequest.setScript("{\"query\": { \"match\" : { \"{{field}}\" : \"{{value}}\" }}}");
1427+
searchTemplateRequest.setScriptType(ScriptType.INLINE);
1428+
searchTemplateRequest.setProfile(randomBoolean());
1429+
1430+
Map<String, Object> scriptParams = new HashMap<>();
1431+
scriptParams.put("field", "name");
1432+
scriptParams.put("value", randomAlphaOfLengthBetween(2, 5));
1433+
searchTemplateRequest.setScriptParams(scriptParams);
1434+
1435+
multiSearchTemplateRequest.add(searchTemplateRequest);
1436+
}
1437+
1438+
Request multiRequest = RequestConverters.multiSearchTemplate(multiSearchTemplateRequest);
1439+
1440+
assertEquals(HttpPost.METHOD_NAME, multiRequest.getMethod());
1441+
assertEquals("/_msearch/template", multiRequest.getEndpoint());
1442+
List<SearchTemplateRequest> searchRequests = multiSearchTemplateRequest.requests();
1443+
assertEquals(numSearchRequests, searchRequests.size());
1444+
1445+
HttpEntity actualEntity = multiRequest.getEntity();
1446+
byte[] expectedBytes = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, XContentType.JSON.xContent());
1447+
assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());
1448+
assertEquals(new BytesArray(expectedBytes), new BytesArray(EntityUtils.toByteArray(actualEntity)));
1449+
}
1450+
14041451
public void testExistsAlias() {
14051452
GetAliasesRequest getAliasesRequest = new GetAliasesRequest();
14061453
String[] indices = randomBoolean() ? null : randomIndicesNames(0, 5);
@@ -2342,7 +2389,7 @@ private static void setRandomSearchParams(SearchRequest searchRequest,
23422389
expectedParams.put("preference", searchRequest.preference());
23432390
}
23442391
if (randomBoolean()) {
2345-
searchRequest.searchType(randomFrom(SearchType.values()));
2392+
searchRequest.searchType(randomFrom(SearchType.CURRENTLY_SUPPORTED));
23462393
}
23472394
expectedParams.put("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT));
23482395
if (randomBoolean()) {

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

+102
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@
5454
import org.elasticsearch.rest.RestStatus;
5555
import org.elasticsearch.script.Script;
5656
import org.elasticsearch.script.ScriptType;
57+
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
58+
import org.elasticsearch.script.mustache.MultiSearchTemplateResponse;
59+
import org.elasticsearch.script.mustache.MultiSearchTemplateResponse.Item;
5760
import org.elasticsearch.script.mustache.SearchTemplateRequest;
5861
import org.elasticsearch.script.mustache.SearchTemplateResponse;
5962
import org.elasticsearch.search.SearchHit;
@@ -890,6 +893,105 @@ public void testRenderSearchTemplate() throws IOException {
890893

891894
assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON);
892895
}
896+
897+
898+
public void testMultiSearchTemplate() throws Exception {
899+
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
900+
901+
SearchTemplateRequest goodRequest = new SearchTemplateRequest();
902+
goodRequest.setRequest(new SearchRequest("index"));
903+
goodRequest.setScriptType(ScriptType.INLINE);
904+
goodRequest.setScript(
905+
"{" +
906+
" \"query\": {" +
907+
" \"match\": {" +
908+
" \"num\": {{number}}" +
909+
" }" +
910+
" }" +
911+
"}");
912+
Map<String, Object> scriptParams = new HashMap<>();
913+
scriptParams.put("number", 10);
914+
goodRequest.setScriptParams(scriptParams);
915+
goodRequest.setExplain(true);
916+
goodRequest.setProfile(true);
917+
multiSearchTemplateRequest.add(goodRequest);
918+
919+
920+
SearchTemplateRequest badRequest = new SearchTemplateRequest();
921+
badRequest.setRequest(new SearchRequest("index"));
922+
badRequest.setScriptType(ScriptType.INLINE);
923+
badRequest.setScript("{ NOT VALID JSON {{number}} }");
924+
scriptParams = new HashMap<>();
925+
scriptParams.put("number", 10);
926+
badRequest.setScriptParams(scriptParams);
927+
928+
multiSearchTemplateRequest.add(badRequest);
929+
930+
MultiSearchTemplateResponse multiSearchTemplateResponse =
931+
execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
932+
highLevelClient()::multiSearchTemplateAsync);
933+
934+
Item[] responses = multiSearchTemplateResponse.getResponses();
935+
936+
assertEquals(2, responses.length);
937+
938+
939+
assertNull(responses[0].getResponse().getSource());
940+
SearchResponse goodResponse =responses[0].getResponse().getResponse();
941+
assertNotNull(goodResponse);
942+
assertThat(responses[0].isFailure(), Matchers.is(false));
943+
assertEquals(1, goodResponse.getHits().totalHits);
944+
assertEquals(1, goodResponse.getHits().getHits().length);
945+
assertThat(goodResponse.getHits().getMaxScore(), greaterThan(0f));
946+
SearchHit hit = goodResponse.getHits().getHits()[0];
947+
assertNotNull(hit.getExplanation());
948+
assertFalse(goodResponse.getProfileResults().isEmpty());
949+
950+
951+
assertNull(responses[0].getResponse().getSource());
952+
assertThat(responses[1].isFailure(), Matchers.is(true));
953+
assertNotNull(responses[1].getFailureMessage());
954+
assertThat(responses[1].getFailureMessage(), containsString("json_parse_exception"));
955+
}
956+
957+
public void testMultiSearchTemplateAllBad() throws Exception {
958+
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
959+
960+
SearchTemplateRequest badRequest1 = new SearchTemplateRequest();
961+
badRequest1.setRequest(new SearchRequest("index"));
962+
badRequest1.setScriptType(ScriptType.INLINE);
963+
badRequest1.setScript(
964+
"{" +
965+
" \"query\": {" +
966+
" \"match\": {" +
967+
" \"num\": {{number}}" +
968+
" }" +
969+
" }" +
970+
"}");
971+
Map<String, Object> scriptParams = new HashMap<>();
972+
scriptParams.put("number", "BAD NUMBER");
973+
badRequest1.setScriptParams(scriptParams);
974+
multiSearchTemplateRequest.add(badRequest1);
975+
976+
977+
SearchTemplateRequest badRequest2 = new SearchTemplateRequest();
978+
badRequest2.setRequest(new SearchRequest("index"));
979+
badRequest2.setScriptType(ScriptType.INLINE);
980+
badRequest2.setScript("BAD QUERY TEMPLATE");
981+
scriptParams = new HashMap<>();
982+
scriptParams.put("number", "BAD NUMBER");
983+
badRequest2.setScriptParams(scriptParams);
984+
985+
multiSearchTemplateRequest.add(badRequest2);
986+
987+
// The whole HTTP request should fail if no nested search requests are valid
988+
ElasticsearchStatusException exception = expectThrows(ElasticsearchStatusException.class,
989+
() -> execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
990+
highLevelClient()::multiSearchTemplateAsync));
991+
992+
assertEquals(RestStatus.BAD_REQUEST, exception.status());
993+
assertThat(exception.getMessage(), containsString("no requests added"));
994+
}
893995

894996
public void testExplain() throws IOException {
895997
{

0 commit comments

Comments
 (0)