Skip to content

Commit 655b2dd

Browse files
committed
Added SearchTemplate support for reactive client
Closes spring-projects#1725
1 parent eb816cc commit 655b2dd

File tree

8 files changed

+135
-1
lines changed

8 files changed

+135
-1
lines changed

Diff for: src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java

+9
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@
9292
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
9393
import org.elasticsearch.rest.BytesRestResponse;
9494
import org.elasticsearch.rest.RestStatus;
95+
import org.elasticsearch.script.mustache.SearchTemplateRequest;
96+
import org.elasticsearch.script.mustache.SearchTemplateResponse;
9597
import org.elasticsearch.search.SearchHit;
9698
import org.elasticsearch.search.SearchHits;
9799
import org.elasticsearch.search.aggregations.Aggregation;
@@ -404,6 +406,13 @@ public Mono<Long> count(HttpHeaders headers, SearchRequest searchRequest) {
404406
.next();
405407
}
406408

409+
@Override
410+
public Flux<SearchHit> searchTemplate(HttpHeaders headers, SearchTemplateRequest searchTemplateRequest) {
411+
return sendRequest(searchTemplateRequest, requestCreator.searchTemplate(), SearchTemplateResponse.class, headers)
412+
.map(r -> r.getResponse().getHits())
413+
.flatMap(Flux::fromIterable);
414+
}
415+
407416
/*
408417
* (non-Javadoc)
409418
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.action.search.SearchRequest)

Diff for: src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java

+24
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.elasticsearch.index.reindex.BulkByScrollResponse;
5353
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
5454
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
55+
import org.elasticsearch.script.mustache.SearchTemplateRequest;
5556
import org.elasticsearch.search.SearchHit;
5657
import org.elasticsearch.search.aggregations.Aggregation;
5758
import org.elasticsearch.search.suggest.Suggest;
@@ -385,6 +386,29 @@ default Mono<Long> count(SearchRequest searchRequest) {
385386
*/
386387
Mono<Long> count(HttpHeaders headers, SearchRequest searchRequest);
387388

389+
/**
390+
* Executes a {@link SearchTemplateRequest} against the {@literal search template} API.
391+
*
392+
* @param searchTemplateRequest must not be {@literal null}.
393+
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html">Search Template
394+
* API on elastic.co</a>
395+
* @return the {@link Flux} emitting {@link SearchHit hits} one by one.
396+
*/
397+
default Flux<SearchHit> searchTemplate(SearchTemplateRequest searchTemplateRequest) {
398+
return searchTemplate(HttpHeaders.EMPTY, searchTemplateRequest);
399+
}
400+
401+
/**
402+
* Executes a {@link SearchTemplateRequest} against the {@literal search template} API.
403+
*
404+
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
405+
* @param searchTemplateRequest must not be {@literal null}.
406+
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html">Search Template
407+
* API on elastic.co</a>
408+
* @return the {@link Flux} emitting {@link SearchHit hits} one by one.
409+
*/
410+
Flux<SearchHit> searchTemplate(HttpHeaders headers, SearchTemplateRequest searchTemplateRequest);
411+
388412
/**
389413
* Execute a {@link SearchRequest} against the {@literal search} API.
390414
*

Diff for: src/main/java/org/springframework/data/elasticsearch/client/reactive/RequestCreator.java

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.elasticsearch.client.indices.PutMappingRequest;
3535
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
3636
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
37+
import org.elasticsearch.script.mustache.SearchTemplateRequest;
3738
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
3839
import org.springframework.data.elasticsearch.client.util.RequestConverters;
3940

@@ -49,6 +50,10 @@ default Function<SearchRequest, Request> search() {
4950
return RequestConverters::search;
5051
}
5152

53+
default Function<SearchTemplateRequest, Request> searchTemplate() {
54+
return RequestConverters::searchTemplate;
55+
}
56+
5257
default Function<SearchScrollRequest, Request> scroll() {
5358
return RequestConverters::searchScroll;
5459
}

Diff for: src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java

+16
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
import org.elasticsearch.index.reindex.ReindexRequest;
9999
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
100100
import org.elasticsearch.index.seqno.SequenceNumbers;
101+
import org.elasticsearch.script.mustache.SearchTemplateRequest;
101102
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
102103
import org.elasticsearch.tasks.TaskId;
103104
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
@@ -411,6 +412,21 @@ public static Request search(SearchRequest searchRequest) {
411412
return request;
412413
}
413414

415+
public static Request searchTemplate(SearchTemplateRequest templateRequest) {
416+
SearchRequest searchRequest = templateRequest.getRequest();
417+
418+
String endpoint = new EndpointBuilder().addCommaSeparatedPathParts(templateRequest.getRequest().indices())
419+
.addPathPart("_search").addPathPart("template").build();
420+
421+
Request request = new Request(HttpMethod.GET.name(), endpoint);
422+
Params params = new Params(request);
423+
addSearchRequestParams(params, searchRequest);
424+
425+
request.setEntity(createEntity(templateRequest, REQUEST_BODY_CONTENT_TYPE));
426+
427+
return request;
428+
}
429+
414430
/**
415431
* Creates a count request.
416432
*

Diff for: src/main/java/org/springframework/data/elasticsearch/core/SearchOperations.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ default long count(Query query, IndexCoordinates index) {
6868
* Does a suggest query
6969
*
7070
* @param suggestion the query
71-
* @param the entity class
71+
* @param clazz the entity class
7272
* @return the suggest response
7373
* @since 4.1
7474
*/

Diff for: src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java

+10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121

2222
import org.elasticsearch.index.query.QueryBuilder;
23+
import org.elasticsearch.script.mustache.SearchTemplateRequestBuilder;
2324
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
2425
import org.elasticsearch.search.collapse.CollapseBuilder;
2526
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
@@ -50,6 +51,7 @@ public class NativeSearchQuery extends AbstractQuery {
5051
@Nullable private HighlightBuilder highlightBuilder;
5152
@Nullable private HighlightBuilder.Field[] highlightFields;
5253
@Nullable private List<IndexBoost> indicesBoost;
54+
@Nullable private SearchTemplateRequestBuilder searchTemplate;
5355

5456
public NativeSearchQuery(@Nullable QueryBuilder query) {
5557

@@ -163,4 +165,12 @@ public void setIndicesBoost(List<IndexBoost> indicesBoost) {
163165
this.indicesBoost = indicesBoost;
164166
}
165167

168+
@Nullable
169+
public SearchTemplateRequestBuilder getSearchTemplate() {
170+
return searchTemplate;
171+
}
172+
173+
public void setSearchTemplate(@Nullable SearchTemplateRequestBuilder searchTemplate) {
174+
this.searchTemplate = searchTemplate;
175+
}
166176
}

Diff for: src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java

+11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.action.support.IndicesOptions;
2626
import org.elasticsearch.common.unit.TimeValue;
2727
import org.elasticsearch.index.query.QueryBuilder;
28+
import org.elasticsearch.script.mustache.SearchTemplateRequestBuilder;
2829
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
2930
import org.elasticsearch.search.collapse.CollapseBuilder;
3031
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
@@ -60,6 +61,7 @@ public class NativeSearchQueryBuilder {
6061
@Nullable private SourceFilter sourceFilter;
6162
@Nullable private CollapseBuilder collapseBuilder;
6263
@Nullable private List<IndexBoost> indicesBoost;
64+
@Nullable private SearchTemplateRequestBuilder searchTemplateBuilder;
6365
private float minScore;
6466
private boolean trackScores;
6567
@Nullable private Collection<String> ids;
@@ -116,6 +118,11 @@ public NativeSearchQueryBuilder withIndicesBoost(List<IndexBoost> indicesBoost)
116118
return this;
117119
}
118120

121+
public NativeSearchQueryBuilder withSearchTemplate(SearchTemplateRequestBuilder searchTemplateBuilder){
122+
this.searchTemplateBuilder = searchTemplateBuilder;
123+
return this;
124+
}
125+
119126
public NativeSearchQueryBuilder withPageable(Pageable pageable) {
120127
this.pageable = pageable;
121128
return this;
@@ -209,6 +216,10 @@ public NativeSearchQuery build() {
209216
nativeSearchQuery.setIndicesBoost(indicesBoost);
210217
}
211218

219+
if (searchTemplateBuilder != null) {
220+
nativeSearchQuery.setSearchTemplate(searchTemplateBuilder);
221+
}
222+
212223
if (!isEmpty(scriptFields)) {
213224
nativeSearchQuery.setScriptFields(scriptFields);
214225
}

Diff for: src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java

+59
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.elasticsearch.rest.RestStatus;
5353
import org.elasticsearch.script.Script;
5454
import org.elasticsearch.script.ScriptType;
55+
import org.elasticsearch.script.mustache.SearchTemplateRequest;
5556
import org.elasticsearch.search.aggregations.AggregationBuilders;
5657
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
5758
import org.elasticsearch.search.builder.SearchSourceBuilder;
@@ -73,6 +74,7 @@
7374
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
7475
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
7576
import org.springframework.http.HttpHeaders;
77+
import org.springframework.http.MediaType;
7678
import org.springframework.test.context.ContextConfiguration;
7779

7880
/**
@@ -434,6 +436,63 @@ public void deleteShouldReturnNotFoundForNonExistingDocument() {
434436
.verifyComplete();
435437
}
436438

439+
@Test // [GH-1725]
440+
public void inlineSearchTemplateShouldFindMatchingDocuments() {
441+
442+
addSourceDocument().to(INDEX_I);
443+
addSourceDocument().to(INDEX_I);
444+
445+
Map<String, Object> testDoc = new LinkedHashMap<>();
446+
testDoc.put("firstname", "inline");
447+
testDoc.put("lastname", "template");
448+
add(testDoc).to(INDEX_I);
449+
450+
SearchTemplateRequest request = new SearchTemplateRequest(new SearchRequest(INDEX_I));
451+
request.setScriptType(ScriptType.INLINE);
452+
request.setScript("{\"query\":{\"match\":{\"firstname\":\"{{firstname}}\"}}}");
453+
Map<String, Object> params = new LinkedHashMap<>();
454+
params.put("firstname", "inline");
455+
request.setScriptParams(params);
456+
457+
client.searchTemplate(request)
458+
.as(StepVerifier::create)
459+
.expectNextCount(1)
460+
.verifyComplete();
461+
}
462+
463+
@Test // [GH-1725]
464+
public void storedSearchTemplateShouldFindMatchingDocuments() {
465+
466+
addSourceDocument().to(INDEX_I);
467+
addSourceDocument().to(INDEX_I);
468+
469+
Map<String, Object> testDoc = new LinkedHashMap<>();
470+
testDoc.put("firstname", "stored");
471+
testDoc.put("lastname", "template");
472+
add(testDoc).to(INDEX_I);
473+
474+
client.execute(c -> c.post()
475+
.uri(builder -> builder.path("_scripts/searchbyfirstname").build())
476+
.contentType(MediaType.APPLICATION_JSON)
477+
.bodyValue(
478+
"{\"script\":{\"lang\":\"mustache\",\"source\":{\"query\":{\"match\":{\"firstname\":\"{{firstname}}\"}}}}}")
479+
.retrieve()
480+
.bodyToMono(Void.class))
481+
.block();
482+
483+
SearchTemplateRequest request = new SearchTemplateRequest(new SearchRequest(INDEX_I));
484+
request.setScriptType(ScriptType.STORED);
485+
request.setScript("searchbyfirstname");
486+
Map<String, Object> params = new LinkedHashMap<>();
487+
params.put("firstname", "stored");
488+
request.setScriptParams(params);
489+
490+
client.searchTemplate(request)
491+
.as(StepVerifier::create)
492+
.expectNextCount(1)
493+
.verifyComplete();
494+
}
495+
437496
@Test // DATAES-488
438497
public void searchShouldFindExistingDocuments() {
439498

0 commit comments

Comments
 (0)