From 8a6fe9a43c64644792148dac54264deb9fbcd5e5 Mon Sep 17 00:00:00 2001 From: Maksim Timonin Date: Sat, 11 Jan 2020 15:41:44 +0300 Subject: [PATCH] Add Get Source API to the HLRC relates: #47678 --- .../client/RequestConverters.java | 18 ++++- .../client/RestHighLevelClient.java | 37 ++++++++++ .../java/org/elasticsearch/client/CrudIT.java | 54 ++++++++++++++ .../client/RestHighLevelClientTests.java | 1 - .../documentation/CRUDDocumentationIT.java | 73 +++++++++++++++++++ .../high-level/document/get-source.asciidoc | 30 ++++++++ .../high-level/supported-apis.asciidoc | 2 + 7 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 docs/java-rest/high-level/document/get-source.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 0b356007822a13..10df75c1f58d82 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -277,14 +277,26 @@ private static Request getStyleRequest(String method, GetRequest getRequest) { static Request sourceExists(GetRequest getRequest) { String endpoint = endpoint(getRequest.index(), "_source", getRequest.id()); Request request = new Request(HttpHead.METHOD_NAME, endpoint); + request.addParameters(sourceParams(getRequest).asMap()); + return request; + } + + static Request getSource(GetRequest getRequest) { + String endpoint = endpoint(getRequest.index(), "_source", getRequest.id()); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + request.addParameters(sourceParams(getRequest).asMap()); + return request; + } + + private static Params sourceParams(GetRequest getRequest) { Params parameters = new Params(); parameters.withPreference(getRequest.preference()); parameters.withRouting(getRequest.routing()); parameters.withRefresh(getRequest.refresh()); parameters.withRealtime(getRequest.realtime()); - // Version params are not currently supported by the source exists API so are not passed - request.addParameters(parameters.asMap()); - return request; + parameters.withFetchSourceContext(getRequest.fetchSourceContext()); + // Version params are not currently supported by the _source API so are not passed + return parameters; } static Request multiGet(MultiGetRequest multiGetRequest) throws IOException { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java index 3986d4e8f13db3..196929d35a58cd 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java @@ -66,6 +66,8 @@ import org.elasticsearch.common.CheckedConsumer; import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.ContextParser; import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; @@ -189,6 +191,7 @@ import java.io.Closeable; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -860,6 +863,31 @@ public final Cancellable existsSourceAsync(GetRequest getRequest, RequestOptions RestHighLevelClient::convertExistsResponse, listener, emptySet()); } + /** + * Retrieves the source field only of a document using GetSource API. + * See Get Source API + * on elastic.co + * @param getRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public BytesReference getSource(GetRequest getRequest, RequestOptions options) throws IOException { + return performRequest(getRequest, RequestConverters::getSource, options, RestHighLevelClient::convertBytesResponse, emptySet()); + } + + /** + * Asynchronously retrieves the source field only of a document using GetSource API. + * See Get Source API + * on elastic.co + * @param getRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return cancellable that may be used to cancel the request + */ + public final Cancellable getSourceAsync(GetRequest getRequest, RequestOptions options, ActionListener listener) { + return performRequestAsync(getRequest, RequestConverters::getSource, options, RestHighLevelClient::convertBytesResponse, listener, emptySet()); + } + /** * Index a document using the Index API. * See Index API on elastic.co @@ -1798,6 +1826,15 @@ protected static boolean convertExistsResponse(Response response) { return response.getStatusLine().getStatusCode() == 200; } + private static BytesReference convertBytesResponse(Response response) throws IOException { + if (response.getEntity() == null) { + throw new IllegalStateException("Response body expected but not returned"); + } + try (InputStream s = response.getEntity().getContent()) { + return new BytesArray(s.readAllBytes()); + } + } + /** * Ignores deprecation warnings. This is appropriate because it is only * used to parse responses from Elasticsearch. Any deprecation warnings diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java index 8abb2fc5b5fcc4..fabec150032f41 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java @@ -359,6 +359,60 @@ public void testMultiGet() throws IOException { } } + public void testGetSource() throws IOException { + { + GetRequest getRequest = new GetRequest("index", "id"); + ElasticsearchException exception = expectThrows(ElasticsearchException.class, + () -> execute(getRequest, highLevelClient()::getSource, highLevelClient()::getSourceAsync)); + assertEquals(RestStatus.NOT_FOUND, exception.status()); + assertEquals("Elasticsearch exception [type=index_not_found_exception, reason=no such index [index]]", exception.getMessage()); + assertEquals("index", exception.getMetadata("es.index").get(0)); + } + IndexRequest index = new IndexRequest("index").id("id"); + String document = "{\"field1\":\"value1\",\"field2\":\"value2\"}"; + index.source(document, XContentType.JSON); + index.setRefreshPolicy(RefreshPolicy.IMMEDIATE); + highLevelClient().index(index, RequestOptions.DEFAULT); + { + GetRequest getRequest = new GetRequest("index", "id"); + BytesReference bytesResponse = execute(getRequest, highLevelClient()::getSource, highLevelClient()::getSourceAsync); + assertEquals(document, bytesResponse.utf8ToString()); + } + { + GetRequest getRequest = new GetRequest("index", "does_not_exist"); + ElasticsearchException exception = expectThrows(ElasticsearchException.class, + () -> execute(getRequest, highLevelClient()::getSource, highLevelClient()::getSourceAsync)); + assertEquals(RestStatus.NOT_FOUND, exception.status()); + assertEquals("Elasticsearch exception [type=resource_not_found_exception, reason=Document not found [index]/[does_not_exist]]", exception.getMessage()); + } + { + GetRequest getRequest = new GetRequest("index", "id"); + getRequest.fetchSourceContext(new FetchSourceContext(true, Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY)); + BytesReference bytesResponse = execute(getRequest, highLevelClient()::getSource, highLevelClient()::getSourceAsync); + assertEquals(document, bytesResponse.utf8ToString()); + } + { + GetRequest getRequest = new GetRequest("index", "id"); + getRequest.fetchSourceContext(new FetchSourceContext(true, new String[]{"field1"}, Strings.EMPTY_ARRAY)); + BytesReference bytesResponse = execute(getRequest, highLevelClient()::getSource, highLevelClient()::getSourceAsync); + assertEquals("{\"field1\":\"value1\"}", bytesResponse.utf8ToString()); + } + { + GetRequest getRequest = new GetRequest("index", "id"); + getRequest.fetchSourceContext(new FetchSourceContext(true, Strings.EMPTY_ARRAY, new String[]{"field1"})); + BytesReference bytesResponse = execute(getRequest, highLevelClient()::getSource, highLevelClient()::getSourceAsync); + assertEquals("{\"field2\":\"value2\"}", bytesResponse.utf8ToString()); + } + { + GetRequest getRequest = new GetRequest("index", "id"); + getRequest.fetchSourceContext(new FetchSourceContext(false)); + ElasticsearchException exception = expectThrows(ElasticsearchException.class, + () -> execute(getRequest, highLevelClient()::getSource, highLevelClient()::getSourceAsync)); + System.out.println(exception.getMessage()); + assertEquals("Elasticsearch exception [type=action_request_validation_exception, reason=Validation Failed: 1: fetching source can not be disabled;]", exception.getMessage()); + } + } + public void testIndex() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java index 4ec048ad1e4f58..f2c2587a166490 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java @@ -777,7 +777,6 @@ public void testApiNamingConventions() throws Exception { "create", "get_script_context", "get_script_languages", - "get_source", "indices.exists_type", "indices.get_upgrade", "indices.put_alias", diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java index b579b81e2eea5b..249156b927d29a 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java @@ -61,6 +61,7 @@ import org.elasticsearch.client.indices.CreateIndexResponse; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; @@ -1398,6 +1399,78 @@ public void onFailure(Exception e) { } } + public void testGetSource() throws Exception { + RestHighLevelClient client = highLevelClient(); + { + Request createIndex = new Request("PUT", "/posts"); + createIndex.setJsonEntity( + "{\n" + + " \"mappings\" : {\n" + + " \"properties\" : {\n" + + " \"message\" : {\n" + + " \"type\": \"text\",\n" + + " \"store\": true\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + Response response = client().performRequest(createIndex); + assertEquals(200, response.getStatusLine().getStatusCode()); + + IndexRequest indexRequest = new IndexRequest("posts").id("1") + .source("user", "kimchy", + "postDate", new Date(), + "message", "trying out Elasticsearch"); + IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT); + assertEquals(DocWriteResponse.Result.CREATED, indexResponse.getResult()); + } + + // tag::get-source-request + GetRequest getSourceRequest = new GetRequest( + "posts", // <1> + "1"); // <2> + + String[] includes = Strings.EMPTY_ARRAY; + String[] excludes = new String[]{"postDate"}; + getSourceRequest.fetchSourceContext(new FetchSourceContext(true, includes, excludes)); + // end::get-source-request + { + // tag::get-source-execute + BytesReference bytesResponse = client.getSource(getSourceRequest, RequestOptions.DEFAULT); + // end::get-source-execute + assertTrue(bytesResponse.utf8ToString().contains("user")); + assertFalse(bytesResponse.utf8ToString().contains("postDate")); + } + { + GetRequest request = new GetRequest("posts", "1"); + + // tag::get-source-execute-listener + ActionListener listener = new ActionListener() { + @Override + public void onResponse(BytesReference getResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::get-source-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + //tag::get-source-execute-async + client.getSourceAsync(request, RequestOptions.DEFAULT, listener); // <1> + //end::get-source-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + + } + public void testExists() throws Exception { RestHighLevelClient client = highLevelClient(); // tag::exists-request diff --git a/docs/java-rest/high-level/document/get-source.asciidoc b/docs/java-rest/high-level/document/get-source.asciidoc new file mode 100644 index 00000000000000..ed63dd973f57fd --- /dev/null +++ b/docs/java-rest/high-level/document/get-source.asciidoc @@ -0,0 +1,30 @@ +-- +:api: get-source +:request: GetRequest +:response: BytesReference +-- + +[id="{upid}-{api}"] +=== Get Source API + +This API helps to get only the `_source` field of a document. + +[id="{upid}-{api}-request"] +==== Get Request + +Request `GetRequest` is the same like a Get API request: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> `FetchSourceContext` 's first argument `fetchSource` must be `true`, otherwise +`ElasticsearchException` get thrown +<2> Arguments `excludes` and `includes` are optional (see examples in Get API documentation) + +include::../execution.asciidoc[] + +[id="{upid}-{api}-response"] +==== Get Source Response + +The returned +{response}+ represents a byte string of the source of requested document. diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index e0d228b5d1e4ba..494372c7fab34b 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -11,6 +11,7 @@ The Java High Level REST Client supports the following Document APIs: Single document APIs:: * <<{upid}-index>> * <<{upid}-get>> +* <<{upid}-get-source>> * <<{upid}-exists>> * <<{upid}-delete>> * <<{upid}-update>> @@ -28,6 +29,7 @@ Multi-document APIs:: include::document/index.asciidoc[] include::document/get.asciidoc[] +include::document/get-source.asciidoc[] include::document/exists.asciidoc[] include::document/delete.asciidoc[] include::document/update.asciidoc[]