From 655b2dd348b0c5077a9a35ad17571d5cc0107ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Vasek?= Date: Sun, 14 Mar 2021 16:12:14 +0100 Subject: [PATCH 1/4] Added SearchTemplate support for reactive client Closes #1725 --- .../DefaultReactiveElasticsearchClient.java | 9 +++ .../reactive/ReactiveElasticsearchClient.java | 24 ++++++++ .../client/reactive/RequestCreator.java | 5 ++ .../client/util/RequestConverters.java | 16 +++++ .../elasticsearch/core/SearchOperations.java | 2 +- .../core/query/NativeSearchQuery.java | 10 ++++ .../core/query/NativeSearchQueryBuilder.java | 11 ++++ ...veElasticsearchClientIntegrationTests.java | 59 +++++++++++++++++++ 8 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java index af02927b7..8e7f6a08f 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/DefaultReactiveElasticsearchClient.java @@ -92,6 +92,8 @@ import org.elasticsearch.index.reindex.UpdateByQueryRequest; import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.script.mustache.SearchTemplateRequest; +import org.elasticsearch.script.mustache.SearchTemplateResponse; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.Aggregation; @@ -404,6 +406,13 @@ public Mono count(HttpHeaders headers, SearchRequest searchRequest) { .next(); } + @Override + public Flux searchTemplate(HttpHeaders headers, SearchTemplateRequest searchTemplateRequest) { + return sendRequest(searchTemplateRequest, requestCreator.searchTemplate(), SearchTemplateResponse.class, headers) + .map(r -> r.getResponse().getHits()) + .flatMap(Flux::fromIterable); + } + /* * (non-Javadoc) * @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.action.search.SearchRequest) diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java index dcc430758..e32ed8245 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java @@ -52,6 +52,7 @@ import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.index.reindex.UpdateByQueryRequest; +import org.elasticsearch.script.mustache.SearchTemplateRequest; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.suggest.Suggest; @@ -385,6 +386,29 @@ default Mono count(SearchRequest searchRequest) { */ Mono count(HttpHeaders headers, SearchRequest searchRequest); + /** + * Executes a {@link SearchTemplateRequest} against the {@literal search template} API. + * + * @param searchTemplateRequest must not be {@literal null}. + * @see Search Template + * API on elastic.co + * @return the {@link Flux} emitting {@link SearchHit hits} one by one. + */ + default Flux searchTemplate(SearchTemplateRequest searchTemplateRequest) { + return searchTemplate(HttpHeaders.EMPTY, searchTemplateRequest); + } + + /** + * Executes a {@link SearchTemplateRequest} against the {@literal search template} API. + * + * @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}. + * @param searchTemplateRequest must not be {@literal null}. + * @see Search Template + * API on elastic.co + * @return the {@link Flux} emitting {@link SearchHit hits} one by one. + */ + Flux searchTemplate(HttpHeaders headers, SearchTemplateRequest searchTemplateRequest); + /** * Execute a {@link SearchRequest} against the {@literal search} API. * diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/RequestCreator.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/RequestCreator.java index abe82762d..1d5363137 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/RequestCreator.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/RequestCreator.java @@ -34,6 +34,7 @@ import org.elasticsearch.client.indices.PutMappingRequest; import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.index.reindex.UpdateByQueryRequest; +import org.elasticsearch.script.mustache.SearchTemplateRequest; import org.springframework.data.elasticsearch.UncategorizedElasticsearchException; import org.springframework.data.elasticsearch.client.util.RequestConverters; @@ -49,6 +50,10 @@ default Function search() { return RequestConverters::search; } + default Function searchTemplate() { + return RequestConverters::searchTemplate; + } + default Function scroll() { return RequestConverters::searchScroll; } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java b/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java index cc0e81811..dadbd4d79 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/util/RequestConverters.java @@ -98,6 +98,7 @@ import org.elasticsearch.index.reindex.ReindexRequest; import org.elasticsearch.index.reindex.UpdateByQueryRequest; import org.elasticsearch.index.seqno.SequenceNumbers; +import org.elasticsearch.script.mustache.SearchTemplateRequest; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import org.elasticsearch.tasks.TaskId; import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient; @@ -411,6 +412,21 @@ public static Request search(SearchRequest searchRequest) { return request; } + public static Request searchTemplate(SearchTemplateRequest templateRequest) { + SearchRequest searchRequest = templateRequest.getRequest(); + + String endpoint = new EndpointBuilder().addCommaSeparatedPathParts(templateRequest.getRequest().indices()) + .addPathPart("_search").addPathPart("template").build(); + + Request request = new Request(HttpMethod.GET.name(), endpoint); + Params params = new Params(request); + addSearchRequestParams(params, searchRequest); + + request.setEntity(createEntity(templateRequest, REQUEST_BODY_CONTENT_TYPE)); + + return request; + } + /** * Creates a count request. * diff --git a/src/main/java/org/springframework/data/elasticsearch/core/SearchOperations.java b/src/main/java/org/springframework/data/elasticsearch/core/SearchOperations.java index 04c9b38d3..2de504a38 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/SearchOperations.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/SearchOperations.java @@ -68,7 +68,7 @@ default long count(Query query, IndexCoordinates index) { * Does a suggest query * * @param suggestion the query - * @param the entity class + * @param clazz the entity class * @return the suggest response * @since 4.1 */ diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java index 8c592650d..5201ea73b 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java @@ -20,6 +20,7 @@ import java.util.List; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.script.mustache.SearchTemplateRequestBuilder; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.collapse.CollapseBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; @@ -50,6 +51,7 @@ public class NativeSearchQuery extends AbstractQuery { @Nullable private HighlightBuilder highlightBuilder; @Nullable private HighlightBuilder.Field[] highlightFields; @Nullable private List indicesBoost; + @Nullable private SearchTemplateRequestBuilder searchTemplate; public NativeSearchQuery(@Nullable QueryBuilder query) { @@ -163,4 +165,12 @@ public void setIndicesBoost(List indicesBoost) { this.indicesBoost = indicesBoost; } + @Nullable + public SearchTemplateRequestBuilder getSearchTemplate() { + return searchTemplate; + } + + public void setSearchTemplate(@Nullable SearchTemplateRequestBuilder searchTemplate) { + this.searchTemplate = searchTemplate; + } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java index 45af3f1f5..2fcf3288c 100755 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java @@ -25,6 +25,7 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.script.mustache.SearchTemplateRequestBuilder; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.collapse.CollapseBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; @@ -60,6 +61,7 @@ public class NativeSearchQueryBuilder { @Nullable private SourceFilter sourceFilter; @Nullable private CollapseBuilder collapseBuilder; @Nullable private List indicesBoost; + @Nullable private SearchTemplateRequestBuilder searchTemplateBuilder; private float minScore; private boolean trackScores; @Nullable private Collection ids; @@ -116,6 +118,11 @@ public NativeSearchQueryBuilder withIndicesBoost(List indicesBoost) return this; } + public NativeSearchQueryBuilder withSearchTemplate(SearchTemplateRequestBuilder searchTemplateBuilder){ + this.searchTemplateBuilder = searchTemplateBuilder; + return this; + } + public NativeSearchQueryBuilder withPageable(Pageable pageable) { this.pageable = pageable; return this; @@ -209,6 +216,10 @@ public NativeSearchQuery build() { nativeSearchQuery.setIndicesBoost(indicesBoost); } + if (searchTemplateBuilder != null) { + nativeSearchQuery.setSearchTemplate(searchTemplateBuilder); + } + if (!isEmpty(scriptFields)) { nativeSearchQuery.setScriptFields(scriptFields); } diff --git a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java index 1bfa6053a..f0d40740b 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; +import org.elasticsearch.script.mustache.SearchTemplateRequest; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -73,6 +74,7 @@ import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration; import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; /** @@ -434,6 +436,63 @@ public void deleteShouldReturnNotFoundForNonExistingDocument() { .verifyComplete(); } + @Test // [GH-1725] + public void inlineSearchTemplateShouldFindMatchingDocuments() { + + addSourceDocument().to(INDEX_I); + addSourceDocument().to(INDEX_I); + + Map testDoc = new LinkedHashMap<>(); + testDoc.put("firstname", "inline"); + testDoc.put("lastname", "template"); + add(testDoc).to(INDEX_I); + + SearchTemplateRequest request = new SearchTemplateRequest(new SearchRequest(INDEX_I)); + request.setScriptType(ScriptType.INLINE); + request.setScript("{\"query\":{\"match\":{\"firstname\":\"{{firstname}}\"}}}"); + Map params = new LinkedHashMap<>(); + params.put("firstname", "inline"); + request.setScriptParams(params); + + client.searchTemplate(request) + .as(StepVerifier::create) + .expectNextCount(1) + .verifyComplete(); + } + + @Test // [GH-1725] + public void storedSearchTemplateShouldFindMatchingDocuments() { + + addSourceDocument().to(INDEX_I); + addSourceDocument().to(INDEX_I); + + Map testDoc = new LinkedHashMap<>(); + testDoc.put("firstname", "stored"); + testDoc.put("lastname", "template"); + add(testDoc).to(INDEX_I); + + client.execute(c -> c.post() + .uri(builder -> builder.path("_scripts/searchbyfirstname").build()) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue( + "{\"script\":{\"lang\":\"mustache\",\"source\":{\"query\":{\"match\":{\"firstname\":\"{{firstname}}\"}}}}}") + .retrieve() + .bodyToMono(Void.class)) + .block(); + + SearchTemplateRequest request = new SearchTemplateRequest(new SearchRequest(INDEX_I)); + request.setScriptType(ScriptType.STORED); + request.setScript("searchbyfirstname"); + Map params = new LinkedHashMap<>(); + params.put("firstname", "stored"); + request.setScriptParams(params); + + client.searchTemplate(request) + .as(StepVerifier::create) + .expectNextCount(1) + .verifyComplete(); + } + @Test // DATAES-488 public void searchShouldFindExistingDocuments() { From 6d7055fe64ed76234ba075c5e67a160c4355bc51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Vasek?= Date: Tue, 16 Mar 2021 15:55:14 +0100 Subject: [PATCH 2/4] Update src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java Co-authored-by: Peter-Josef Meisch --- .../reactive/ReactiveElasticsearchClientIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java index f0d40740b..d84389419 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java @@ -460,7 +460,7 @@ public void inlineSearchTemplateShouldFindMatchingDocuments() { .verifyComplete(); } - @Test // [GH-1725] + @Test // #1725 public void storedSearchTemplateShouldFindMatchingDocuments() { addSourceDocument().to(INDEX_I); From 0a033a22e2e6e2586eb14662ee6efed54cda8983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Vasek?= Date: Tue, 16 Mar 2021 15:55:27 +0100 Subject: [PATCH 3/4] Update src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java Co-authored-by: Peter-Josef Meisch --- .../reactive/ReactiveElasticsearchClientIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java index d84389419..3e866f275 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientIntegrationTests.java @@ -436,7 +436,7 @@ public void deleteShouldReturnNotFoundForNonExistingDocument() { .verifyComplete(); } - @Test // [GH-1725] + @Test // #1725 public void inlineSearchTemplateShouldFindMatchingDocuments() { addSourceDocument().to(INDEX_I); From b70971c1f9426f034be40401b51bd549780d93b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Vasek?= Date: Tue, 16 Mar 2021 17:57:46 +0100 Subject: [PATCH 4/4] Added method with SearchTemplateRequest consumer --- .../reactive/ReactiveElasticsearchClient.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java b/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java index e32ed8245..9e407c10f 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java @@ -386,6 +386,20 @@ default Mono count(SearchRequest searchRequest) { */ Mono count(HttpHeaders headers, SearchRequest searchRequest); + /** + * Executes a {@link SearchTemplateRequest} against the {@literal search template} API. + * + * @param consumer must not be {@literal null}. + * @see Search Template + * API on elastic.co + * @return the {@link Flux} emitting {@link SearchHit hits} one by one. + */ + default Flux searchTemplate(Consumer consumer) { + SearchTemplateRequest request = new SearchTemplateRequest(); + consumer.accept(request); + return searchTemplate(request); + } + /** * Executes a {@link SearchTemplateRequest} against the {@literal search template} API. *