From 93e785ac51fef32621268a8b96cb7589447b09d1 Mon Sep 17 00:00:00 2001 From: zh32 Date: Thu, 18 Jun 2020 23:38:26 +0200 Subject: [PATCH] DATAES-866 - Implement suggest query in reactive client. --- .../DefaultReactiveElasticsearchClient.java | 8 +++++ .../reactive/ReactiveElasticsearchClient.java | 23 +++++++++++++++ .../core/ReactiveElasticsearchTemplate.java | 20 +++++++++++++ .../core/ReactiveSearchOperations.java | 21 ++++++++++++++ .../ReactiveElasticsearchClientTests.java | 29 +++++++++++++++++++ 5 files changed, 101 insertions(+) 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 e7dc921fce..bec90fadc2 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 @@ -96,6 +96,7 @@ import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.suggest.Suggest; import org.reactivestreams.Publisher; import org.springframework.data.elasticsearch.client.ClientConfiguration; @@ -132,6 +133,7 @@ * @author Henrique Amaral * @author Roman Puchkovskiy * @author Russell Parry + * @author Thomas Geese * @since 3.2 * @see ClientConfiguration * @see ReactiveRestClients @@ -886,6 +888,12 @@ private static ElasticsearchException getElasticsearchException(String content, } } + @Override + public Flux suggest(HttpHeaders headers, SearchRequest searchRequest) { + return sendRequest(searchRequest, requestCreator.search(), SearchResponse.class, headers) // + .map(SearchResponse::getSuggest); + } + private static void buildExceptionMessages(StringBuilder sb, Throwable t) { sb.append(t.getMessage()); 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 1933907332..6c09f685bc 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.DeleteByQueryRequest; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.suggest.Suggest; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.ElasticsearchHost; import org.springframework.http.HttpHeaders; @@ -67,6 +68,7 @@ * @author Mark Paluch * @author Peter-Josef Meisch * @author Henrique Amaral + * @author Thomas Geese * @since 3.2 * @see ClientConfiguration * @see ReactiveRestClients @@ -417,6 +419,27 @@ default Flux search(SearchRequest searchRequest) { */ Flux search(HttpHeaders headers, SearchRequest searchRequest); + /** + * Execute the given {@link SearchRequest} against the {@literal search} API. + * + * @param searchRequest must not be {@literal null}. + * @return the {@link Flux} emitting {@link Suggest suggestions} one by one. + * @since 4.1 + */ + default Flux suggest(SearchRequest searchRequest) { + return suggest(HttpHeaders.EMPTY, searchRequest); + } + + /** + * Execute the given {@link SearchRequest} against the {@literal search} API. + * + * @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}. + * @param searchRequest must not be {@literal null}. + * @return the {@link Flux} emitting {@link Suggest suggestions} one by one. + * @since 4.1 + */ + Flux suggest(HttpHeaders headers, SearchRequest searchRequest); + /** * Execute the given {@link SearchRequest} with aggregations against the {@literal search} API. * diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplate.java index dbb401e902..a03d4b351f 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplate.java @@ -41,6 +41,8 @@ import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.search.suggest.SuggestBuilder; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,6 +92,7 @@ * @author Aleksei Arsenev * @author Roman Puchkovskiy * @author Russell Parry + * @author Thomas Geese * @since 3.2 */ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOperations, ApplicationContextAware { @@ -640,6 +643,23 @@ public Flux aggregate(Query query, Class entityType, IndexCoordi return doAggregate(query, entityType, index); } + @Override + public Flux suggest(SuggestBuilder suggestion, Class entityType) { + return suggest(suggestion, getIndexCoordinatesFor(entityType)); + } + + @Override + public Flux suggest(SuggestBuilder suggestion, IndexCoordinates index) { + return doSuggest(suggestion, index); + } + + private Flux doSuggest(SuggestBuilder suggestion, IndexCoordinates index) { + return Flux.defer(() -> { + SearchRequest request = requestFactory.searchRequest(suggestion, index); + return Flux.from(execute(client -> client.suggest(request))); + }); + } + private Flux doAggregate(Query query, Class entityType, IndexCoordinates index) { return Flux.defer(() -> { SearchRequest request = requestFactory.searchRequest(query, entityType, index); diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ReactiveSearchOperations.java b/src/main/java/org/springframework/data/elasticsearch/core/ReactiveSearchOperations.java index 47eada0dee..18127c3e9b 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ReactiveSearchOperations.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ReactiveSearchOperations.java @@ -20,6 +20,8 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.search.suggest.SuggestBuilder; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.query.Query; @@ -32,6 +34,7 @@ * * @author Peter-Josef Meisch * @author Russell Parry + * @author Thomas Geese * @since 4.0 */ public interface ReactiveSearchOperations { @@ -206,4 +209,22 @@ default Flux> search(Query query, Class entityType, IndexCoo * @since 4.0 */ Flux aggregate(Query query, Class entityType, IndexCoordinates index); + + /** + * Does a suggest query + * + * @param suggestion the query + * @param entityType must not be {@literal null}. + * @return the suggest response + */ + Flux suggest(SuggestBuilder suggestion, Class entityType); + + /** + * Does a suggest query + * + * @param suggestion the query + * @param index the index to run the query against + * @return the suggest response + */ + Flux suggest(SuggestBuilder suggestion, IndexCoordinates index); } diff --git a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java index 84e0eb9f98..2183841195 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java @@ -53,6 +53,8 @@ import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.suggest.SuggestBuilder; +import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -71,6 +73,7 @@ * @author Peter-Josef Meisch * @author Henrique Amaral * @author Russell Parry + * @author Thomas Geese */ @SpringIntegrationTest @ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class }) @@ -681,6 +684,32 @@ public void aggregateReturnsAggregationResults() throws IOException { .expectNextMatches(aggregation -> aggregation.getType().equals(StringTerms.NAME)).verifyComplete(); } + @Test // DATAES-866 + public void suggestReturnsSuggestionResults() throws IOException { + syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT); + Map jsonMap = Collections.singletonMap("properties", + Collections.singletonMap("firstname", Collections.singletonMap("type", "completion"))); + syncClient.indices().putMapping(new PutMappingRequest(INDEX_I).source(jsonMap), RequestOptions.DEFAULT); + + addSourceDocument().ofType(TYPE_I).to(INDEX_I); + + SuggestBuilder suggestBuilder = new SuggestBuilder().addSuggestion( + "firstname", + new CompletionSuggestionBuilder("firstname").prefix("ch") + ); + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()); + searchSourceBuilder.suggest(suggestBuilder); + + SearchRequest request = new SearchRequest(INDEX_I) // + .source(searchSourceBuilder); + + client + .suggest(request).as(StepVerifier::create).expectNextMatches(suggestions -> suggestions + .getSuggestion("firstname").getEntries().get(0).getOptions().get(0).getText().string().equals("chade")) + .verifyComplete(); + } + private AddToIndexOfType addSourceDocument() { return add(DOC_SOURCE); }