From f3e7a1c4a42a797d093671264b64ae085e10195f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 12 Jul 2017 17:06:46 +0200 Subject: [PATCH] Adding basic search request documentation for high level client (#25651) --- .../documentation/SearchDocumentationIT.java | 136 +++++++++++++ .../org/elasticsearch/search/SearchHit.java | 2 +- .../java-rest/high-level/apis/search.asciidoc | 178 +++++++++++++++++- 3 files changed, 314 insertions(+), 2 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java index 0be7124ec9f..8d94c68e629 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java @@ -28,20 +28,28 @@ import org.elasticsearch.action.search.ClearScrollResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchScrollRequest; +import org.elasticsearch.action.search.ShardSearchFailure; +import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.ESRestHighLevelClientTestCase; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.Scroll; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.ScoreSortBuilder; +import org.elasticsearch.search.sort.SortOrder; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; import static org.hamcrest.Matchers.greaterThan; @@ -63,6 +71,134 @@ import static org.hamcrest.Matchers.greaterThan; */ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase { + @SuppressWarnings({ "unused", "unchecked" }) + public void testSearch() throws IOException { + RestHighLevelClient client = highLevelClient(); + { + BulkRequest request = new BulkRequest(); + request.add(new IndexRequest("posts", "doc", "1") + .source(XContentType.JSON, "title", "In which order are my Elasticsearch queries executed?", "user", + Arrays.asList("kimchy", "luca"), "innerObject", Collections.singletonMap("key", "value"))); + request.add(new IndexRequest("posts", "doc", "2") + .source(XContentType.JSON, "title", "Current status and upcoming changes in Elasticsearch", "user", + Arrays.asList("kimchy", "christoph"), "innerObject", Collections.singletonMap("key", "value"))); + request.add(new IndexRequest("posts", "doc", "3") + .source(XContentType.JSON, "title", "The Future of Federated Search in Elasticsearch", "user", + Arrays.asList("kimchy", "tanguy"), "innerObject", Collections.singletonMap("key", "value"))); + request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); + BulkResponse bulkResponse = client.bulk(request); + assertSame(bulkResponse.status(), RestStatus.OK); + assertFalse(bulkResponse.hasFailures()); + } + { + // tag::search-request-basic + SearchRequest searchRequest = new SearchRequest(); // <1> + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // <2> + searchSourceBuilder.query(QueryBuilders.matchAllQuery()); // <3> + // end::search-request-basic + } + { + // tag::search-request-indices-types + SearchRequest searchRequest = new SearchRequest("posts"); + searchRequest.types("doc"); + // end::search-request-indices-types + // tag::search-request-routing + searchRequest.routing("routing"); // <1> + // end::search-request-routing + // tag::search-request-indicesOptions + searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1> + // end::search-request-indicesOptions + // tag::search-request-preference + searchRequest.preference("_local"); // <1> + // end::search-request-preference + assertNotNull(client.search(searchRequest)); + } + { + // tag::search-source-basics + SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); // <1> + sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy")); // <2> + sourceBuilder.from(0); // <3> + sourceBuilder.size(5); // <4> + sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.ASC)); + sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); // <5> + // end::search-source-basics + + // tag::search-source-setter + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(sourceBuilder); + // end::search-source-setter + + // tag::search-execute + SearchResponse searchResponse = client.search(searchRequest); + // end::search-execute + + // tag::search-execute-async + client.searchAsync(searchRequest, new ActionListener() { + @Override + public void onResponse(SearchResponse searchResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }); + // end::search-execute-async + + // tag::search-response-1 + RestStatus status = searchResponse.status(); + TimeValue took = searchResponse.getTook(); + Boolean terminatedEarly = searchResponse.isTerminatedEarly(); + boolean timedOut = searchResponse.isTimedOut(); + // end::search-response-1 + + // tag::search-response-2 + int totalShards = searchResponse.getTotalShards(); + int successfulShards = searchResponse.getSuccessfulShards(); + int failedShards = searchResponse.getFailedShards(); + for (ShardSearchFailure failure : searchResponse.getShardFailures()) { + // failures should be handled here + } + // end::search-response-2 + assertNotNull(searchResponse); + + // tag::search-hits-get + SearchHits hits = searchResponse.getHits(); + // end::search-hits-get + // tag::search-hits-info + long totalHits = hits.getTotalHits(); + float maxScore = hits.getMaxScore(); + // end::search-hits-info + // tag::search-hits-singleHit + SearchHit[] searchHits = hits.getHits(); + for (SearchHit hit : searchHits) { + // do something with the SearchHit + } + // end::search-hits-singleHit + for (SearchHit hit : searchHits) { + // tag::search-hits-singleHit-properties + String index = hit.getIndex(); + String type = hit.getType(); + String id = hit.getId(); + float score = hit.getScore(); + // end::search-hits-singleHit-properties + // tag::search-hits-singleHit-source + String sourceAsString = hit.getSourceAsString(); + Map sourceAsMap = hit.getSourceAsMap(); + String documentTitle = (String) sourceAsMap.get("title"); + List users = (List) sourceAsMap.get("user"); + Map innerObject = (Map) sourceAsMap.get("innerObject"); + // end::search-hits-singleHit-source + } + assertEquals(3, totalHits); + assertNotNull(hits.getHits()[0].getSourceAsString()); + assertNotNull(hits.getHits()[0].getSourceAsMap().get("title")); + assertNotNull(hits.getHits()[0].getSourceAsMap().get("user")); + assertNotNull(hits.getHits()[0].getSourceAsMap().get("innerObject")); + } + } + public void testScroll() throws IOException { RestHighLevelClient client = highLevelClient(); { diff --git a/core/src/main/java/org/elasticsearch/search/SearchHit.java b/core/src/main/java/org/elasticsearch/search/SearchHit.java index db7a6cf8fdb..45510ea1af9 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchHit.java +++ b/core/src/main/java/org/elasticsearch/search/SearchHit.java @@ -192,7 +192,7 @@ public final class SearchHit implements Streamable, ToXContentObject, Iterable Creates the `SeachRequest`. Without arguments this runs against all indices. +<2> Most parameters of the search can be added to the `SearchSourceBuilder` +which contains everything that +in the Rest API would be placed in the search request body. +<3> Add a `match_all` query to the `SearchSourceBuilder`. + +==== Optional arguments + +Lets first look at some of the optional argument of a `SearchRequest`. +First of all, the request can be restricted to one or more indices using the +constructor or to on or more types using a setter: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-request-indices-types] +-------------------------------------------------- + +There are a couple of other interesting optional parameters: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-request-routing] +-------------------------------------------------- +<1> Set a routing parameter + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-request-indicesOptions] +-------------------------------------------------- +<1> Setting `IndicesOptions` controls how unavailable indices are resolved and +how wildcard expressions are expanded + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-request-preference] +-------------------------------------------------- +<1> Use the preference parameter e.g. to execute the search to prefer local +shards. The The default is to randomize across shards. + +==== Using the SearchSourceBuilder + +Most options controlling the search behavior can be set on the +`SearchSourceBuilder`, +which contains more or less the equivalent of the options in the search request +body of the Rest API. + +Here are a few examples of some common options: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-source-basics] +-------------------------------------------------- +<1> Create a `SearchSourceBuilder` with default options. +<2> Set the query. Can be any type of `QueryBuilder` +<3> Set the `from` option that determines the result index to start searching +from. Defaults to 0. +<4> Set the `size` option that determines the number of search hits to return. +Defaults to 10. +<5> Set an optional timeout that controls how long the search is allowed to +take. + +After this, the `SearchSourceBuilder` only needs to be added to the +`SearchRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-source-setter] +-------------------------------------------------- + + +[[java-rest-high-document-search-sync]] +==== Synchronous Execution + +When executing a `SearchRequest` in the following manner, the client waits +for the `SearchResponse` to be returned before continuing with code execution: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-execute] +-------------------------------------------------- + +[[java-rest-high-document-search-async]] +==== Asynchronous Execution + + +Executing a `SearchRequest` can also be done in an asynchronous fashion so that +the client can return directly. Users need to specify how the response or +potential failures will be handled by passing in appropriate listeners: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-execute-async] +-------------------------------------------------- +<1> Called when the execution is successfully completed. +<2> Called when the whole `SearchRequest` fails. + +==== SearchResponse + +The `SearchResponse` that is returned by executing the search provides details +about the search execution itself as well as access to the documents returned. +First, there is useful information about the request execution itself, like the +HTTP status code, execution time or wether the request terminated early or timed +out: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-response-1] +-------------------------------------------------- + +Second, the response also provides information about the execution on the +shard level by offering statistics about the total number of shards that were +affected by the search, and the successful vs. unsuccessful shards. Possible +failures can also be handled by iterating over an array off +`ShardSearchFailures` like in the following example: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-response-2] +-------------------------------------------------- + +To get access to the returned documents, we need to first get the `SearchHits` +contained in the response: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-hits-get] +-------------------------------------------------- + +The `SearchHits` provides global information about all hits, like total number +of hits or the maximum score: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-hits-info] +-------------------------------------------------- + +Nested inside the `SearchHits` are the individual search results that can +be iterated over like this: + + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-hits-singleHit] +-------------------------------------------------- + +The `SearchHit` provides access to basic information like index, type, docId and +score of each search hit: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-hits-singleHit-properties] +-------------------------------------------------- + +Furthermore, it lets you get back the document source, either as a simple +JSON-String or as a map of key/value pairs. In this map, regular fields +are keyed by the field name and contain the field value. Multi-valued fields are +returned as lists of objects, nested objects as another key/value map. These +cases need to be case accordingly: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SearchDocumentationIT.java[search-hits-singleHit-source] +--------------------------------------------------