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 e35e17da588..ebebfade776 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 @@ -19,6 +19,8 @@ package org.elasticsearch.client.documentation; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NStringEntity; import org.elasticsearch.ElasticsearchException; @@ -48,7 +50,10 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.get.GetResult; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import java.io.IOException; @@ -57,6 +62,9 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; + /** * This class is used to generate the Java CRUD API documentation. * You need to wrap your code between two tags like: @@ -72,7 +80,7 @@ import java.util.Map; * -------------------------------------------------- */ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase { - + public void testIndex() throws IOException { RestHighLevelClient client = highLevelClient(); @@ -80,8 +88,8 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase { //tag::index-request-map Map jsonMap = new HashMap<>(); jsonMap.put("user", "kimchy"); - jsonMap.put("postDate",new Date()); - jsonMap.put("message","trying out Elasticsearch"); + jsonMap.put("postDate", new Date()); + jsonMap.put("message", "trying out Elasticsearch"); IndexRequest indexRequest = new IndexRequest("posts", "doc", "1") .source(jsonMap); // <1> //end::index-request-map @@ -211,7 +219,6 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase { } } // end::index-conflict - } { // tag::index-optype @@ -229,6 +236,263 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase { } } + public void testUpdate() throws IOException { + RestHighLevelClient client = highLevelClient(); + { + IndexRequest indexRequest = new IndexRequest("posts", "doc", "1").source("field", 0); + IndexResponse indexResponse = client.index(indexRequest); + assertSame(indexResponse.status(), RestStatus.CREATED); + + XContentType xContentType = XContentType.JSON; + String script = XContentBuilder.builder(xContentType.xContent()) + .startObject() + .startObject("script") + .field("lang", "painless") + .field("code", "ctx._source.field += params.count") + .endObject() + .endObject().string(); + HttpEntity body = new NStringEntity(script, ContentType.create(xContentType.mediaType())); + Response response = client().performRequest(HttpPost.METHOD_NAME, "/_scripts/increment-field", emptyMap(), body); + assertEquals(response.getStatusLine().getStatusCode(), RestStatus.OK.getStatus()); + } + { + //tag::update-request + UpdateRequest request = new UpdateRequest( + "posts", // <1> + "doc", // <2> + "1"); // <3> + //end::update-request + request.fetchSource(true); + //tag::update-request-with-inline-script + Map parameters = singletonMap("count", 4); // <1> + + Script inline = new Script(ScriptType.INLINE, "painless", "ctx._source.field += params.count", parameters); // <2> + request.script(inline); // <3> + //end::update-request-with-inline-script + UpdateResponse updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + assertEquals(4, updateResponse.getGetResult().getSource().get("field")); + + request = new UpdateRequest("posts", "doc", "1").fetchSource(true); + //tag::update-request-with-stored-script + Script stored = + new Script(ScriptType.STORED, "painless", "increment-field", parameters); // <1> + request.script(stored); // <2> + //end::update-request-with-stored-script + updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + assertEquals(8, updateResponse.getGetResult().getSource().get("field")); + } + { + //tag::update-request-with-doc-as-map + Map jsonMap = new HashMap<>(); + jsonMap.put("updated", new Date()); + jsonMap.put("reason", "daily update"); + UpdateRequest request = new UpdateRequest("posts", "doc", "1") + .doc(jsonMap); // <1> + //end::update-request-with-doc-as-map + UpdateResponse updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + } + { + //tag::update-request-with-doc-as-xcontent + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + { + builder.field("updated", new Date()); + builder.field("reason", "daily update"); + } + builder.endObject(); + UpdateRequest request = new UpdateRequest("posts", "doc", "1") + .doc(builder); // <1> + //end::update-request-with-doc-as-xcontent + UpdateResponse updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + } + { + //tag::update-request-shortcut + UpdateRequest request = new UpdateRequest("posts", "doc", "1") + .doc("updated", new Date(), + "reason", "daily update"); // <1> + //end::update-request-shortcut + UpdateResponse updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + } + { + //tag::update-request-with-doc-as-string + UpdateRequest request = new UpdateRequest("posts", "doc", "1"); + String jsonString = "{" + + "\"updated\":\"2017-01-01\"," + + "\"reason\":\"daily update\"" + + "}"; + request.doc(jsonString, XContentType.JSON); // <1> + //end::update-request-with-doc-as-string + request.fetchSource(true); + // tag::update-execute + UpdateResponse updateResponse = client.update(request); + // end::update-execute + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + + // tag::update-response + String index = updateResponse.getIndex(); + String type = updateResponse.getType(); + String id = updateResponse.getId(); + long version = updateResponse.getVersion(); + if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) { + // <1> + } else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) { + // <2> + } else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) { + // <3> + } else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) { + // <4> + } + // end::update-response + + // tag::update-getresult + GetResult result = updateResponse.getGetResult(); // <1> + if (result.isExists()) { + String sourceAsString = result.sourceAsString(); // <2> + Map sourceAsMap = result.sourceAsMap(); // <3> + byte[] sourceAsBytes = result.source(); // <4> + } else { + // <5> + } + // end::update-getresult + assertNotNull(result); + assertEquals(3, result.sourceAsMap().size()); + // tag::update-failure + ReplicationResponse.ShardInfo shardInfo = updateResponse.getShardInfo(); + if (shardInfo.getTotal() != shardInfo.getSuccessful()) { + // <1> + } + if (shardInfo.getFailed() > 0) { + for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) { + String reason = failure.reason(); // <2> + } + } + // end::update-failure + + // tag::update-execute-async + client.updateAsync(request, new ActionListener() { + @Override + public void onResponse(UpdateResponse updateResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }); + // end::update-execute-async + } + { + //tag::update-docnotfound + UpdateRequest request = new UpdateRequest("posts", "type", "does_not_exist").doc("field", "value"); + try { + UpdateResponse updateResponse = client.update(request); + } catch (ElasticsearchException e) { + if (e.status() == RestStatus.NOT_FOUND) { + // <1> + } + } + //end::update-docnotfound + } + { + // tag::update-conflict + UpdateRequest request = new UpdateRequest("posts", "doc", "1") + .doc("field", "value") + .version(1); + try { + UpdateResponse updateResponse = client.update(request); + } catch(ElasticsearchException e) { + if (e.status() == RestStatus.CONFLICT) { + // <1> + } + } + // end::update-conflict + } + { + UpdateRequest request = new UpdateRequest("posts", "doc", "1").doc("reason", "no source"); + //tag::update-request-no-source + request.fetchSource(true); // <1> + //end::update-request-no-source + UpdateResponse updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + assertNotNull(updateResponse.getGetResult()); + assertEquals(3, updateResponse.getGetResult().sourceAsMap().size()); + } + { + UpdateRequest request = new UpdateRequest("posts", "doc", "1").doc("reason", "source includes"); + //tag::update-request-source-include + String[] includes = new String[]{"updated", "r*"}; + String[] excludes = Strings.EMPTY_ARRAY; + request.fetchSource(new FetchSourceContext(true, includes, excludes)); // <1> + //end::update-request-source-include + UpdateResponse updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + Map sourceAsMap = updateResponse.getGetResult().sourceAsMap(); + assertEquals(2, sourceAsMap.size()); + assertEquals("source includes", sourceAsMap.get("reason")); + assertTrue(sourceAsMap.containsKey("updated")); + } + { + UpdateRequest request = new UpdateRequest("posts", "doc", "1").doc("reason", "source excludes"); + //tag::update-request-source-exclude + String[] includes = Strings.EMPTY_ARRAY; + String[] excludes = new String[]{"updated"}; + request.fetchSource(new FetchSourceContext(true, includes, excludes)); // <1> + //end::update-request-source-exclude + UpdateResponse updateResponse = client.update(request); + assertEquals(updateResponse.getResult(), DocWriteResponse.Result.UPDATED); + Map sourceAsMap = updateResponse.getGetResult().sourceAsMap(); + assertEquals(2, sourceAsMap.size()); + assertEquals("source excludes", sourceAsMap.get("reason")); + assertTrue(sourceAsMap.containsKey("field")); + } + { + UpdateRequest request = new UpdateRequest("posts", "doc", "id"); + // tag::update-request-routing + request.routing("routing"); // <1> + // end::update-request-routing + // tag::update-request-parent + request.parent("parent"); // <1> + // end::update-request-parent + // tag::update-request-timeout + request.timeout(TimeValue.timeValueSeconds(1)); // <1> + request.timeout("1s"); // <2> + // end::update-request-timeout + // tag::update-request-retry + request.retryOnConflict(3); // <1> + // end::update-request-retry + // tag::update-request-refresh + request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); // <1> + request.setRefreshPolicy("wait_for"); // <2> + // end::update-request-refresh + // tag::update-request-version + request.version(2); // <1> + // end::update-request-version + // tag::update-request-detect-noop + request.detectNoop(false); // <1> + // end::update-request-detect-noop + // tag::update-request-upsert + String jsonString = "{\"created\":\"2017-01-01\"}"; + request.upsert(jsonString, XContentType.JSON); // <1> + // end::update-request-upsert + // tag::update-request-scripted-upsert + request.scriptedUpsert(true); // <1> + // end::update-request-scripted-upsert + // tag::update-request-doc-upsert + request.docAsUpsert(true); // <1> + // end::update-request-doc-upsert + // tag::update-request-active-shards + request.waitForActiveShards(2); // <1> + request.waitForActiveShards(ActiveShardCount.ALL); // <2> + // end::update-request-active-shards + } + } + public void testDelete() throws IOException { RestHighLevelClient client = highLevelClient(); diff --git a/docs/java-rest/high-level/apis.asciidoc b/docs/java-rest/high-level/apis.asciidoc index 2c9bf82cf40..2e503d1ac86 100644 --- a/docs/java-rest/high-level/apis.asciidoc +++ b/docs/java-rest/high-level/apis.asciidoc @@ -6,7 +6,7 @@ The Java High Level REST Client supports the following APIs: * <> * <> * <> -* Update API +* <> .Multi-document APIs * <> diff --git a/docs/java-rest/high-level/apis/_index.asciidoc b/docs/java-rest/high-level/apis/_index.asciidoc index 9600f7cad43..35e335e5533 100644 --- a/docs/java-rest/high-level/apis/_index.asciidoc +++ b/docs/java-rest/high-level/apis/_index.asciidoc @@ -124,7 +124,7 @@ include-tagged::{doc-tests}/CRUDDocumentationIT.java[index-response] -------------------------------------------------- <1> Handle (if needed) the case where the document was created for the first time -<2> Handle (if needed) the case where the document was rewriten as it was +<2> Handle (if needed) the case where the document was rewritten as it was already existing <3> Handle the situation where number of successful shards is less than total shards diff --git a/docs/java-rest/high-level/apis/update.asciidoc b/docs/java-rest/high-level/apis/update.asciidoc index 79c4121b9f2..1689306bb79 100644 --- a/docs/java-rest/high-level/apis/update.asciidoc +++ b/docs/java-rest/high-level/apis/update.asciidoc @@ -1,4 +1,254 @@ [[java-rest-high-document-update]] === Update API -See https://github.com/elastic/elasticsearch/pull/25536 \ No newline at end of file +[[java-rest-high-document-update-request]] +==== Update Request + +An `UpdateRequest` requires the following arguments: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request] +-------------------------------------------------- +<1> Index +<2> Type +<3> Document id + +The Update API allows to update an existing document by using a script +or by passing a partial document. + +==== Updates with a script +The script can be provided as an inline script: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-with-inline-script] +-------------------------------------------------- +<1> Script parameters provided as a `Map` of objects +<2> Create an inline script using the `painless` language and the previous parameters +<3> Sets the script to the update request + +Or as a stored script: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-with-stored-script] +-------------------------------------------------- +<1> Reference to a script stored under the name `increment-field` in the `painless` language +<2> Sets the script in the update request + +==== Updates with a partial document +When using updates with a partial document, the partial document will be merged with the +existing document. + +The partial document can be provided in different ways: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-with-doc-as-string] +-------------------------------------------------- +<1> Partial document source provided as a `String` in JSON format + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-with-doc-as-map] +-------------------------------------------------- +<1> Partial document source provided as a `Map` which gets automatically converted +to JSON format + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-with-doc-as-xcontent] +-------------------------------------------------- +<1> Partial document source provided as an `XContentBuilder` object, the Elasticsearch +built-in helpers to generate JSON content + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-shortcut] +-------------------------------------------------- +<1> Partial document source provided as `Object` key-pairs, which gets converted to +JSON format + +==== Upserts +If the document does not already exist, it is possible to define some content that +will be inserted as a new document using the `upsert` method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-upsert] +-------------------------------------------------- +<1> Upsert document source provided as a `String` + +Similarly to the partial document updates, the content of the `upsert` document +can be defined using methods that accept `String`, `Map`, `XContentBuilder` or +`Object` key-pairs. + +==== Optional arguments +The following arguments can optionally be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-routing] +-------------------------------------------------- +<1> Routing value + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-parent] +-------------------------------------------------- +<1> Parent value + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-timeout] +-------------------------------------------------- +<1> Timeout to wait for primary shard to become available as a `TimeValue` +<2> Timeout to wait for primary shard to become available as a `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-refresh] +-------------------------------------------------- +<1> Refresh policy as a `WriteRequest.RefreshPolicy` instance +<2> Refresh policy as a `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-retry] +-------------------------------------------------- +<1> How many times to retry the update operation if the document to update has +been changed by another operation between the get and indexing phases of the +update operation + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-no-source] +-------------------------------------------------- +<1> Enable source retrieval, disabled by default + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-source-include] +-------------------------------------------------- +<1> Configure source inclusion for specific fields + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-source-exclude] +-------------------------------------------------- +<1> Configure source exclusion for specific fields + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-version] +-------------------------------------------------- +<1> Version + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-detect-noop] +-------------------------------------------------- +<1> Disable the noop detection + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-scripted-upsert] +-------------------------------------------------- +<1> Indicate that the script must run regardless of whether the document exists or not, +ie the script takes care of creating the document if it does not already exist. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-doc-upsert] +-------------------------------------------------- +<1> Indicate that the partial document must be used as the upsert document if it +does not exist yet. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-request-active-shards] +-------------------------------------------------- +<1> Sets the number of shard copies that must be active before proceeding with +the update operation. +<2> Number of shard copies provided as a `ActiveShardCount`: can be `ActiveShardCount.ALL`, +`ActiveShardCount.ONE` or `ActiveShardCount.DEFAULT` (default) + +[[java-rest-high-document-update-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-execute] +-------------------------------------------------- + +[[java-rest-high-document-update-async]] +==== Asynchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-execute-async] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument. +<2> Called in case of failure. The raised exception is provided as an argument. + +[[java-rest-high-document-update-response]] +==== Update Response + +The returned `UpdateResponse` allows to retrieve information about the executed + operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-response] +-------------------------------------------------- +<1> Handle the case where the document was created for the first time (upsert) +<2> Handle the case where the document was updated +<3> Handle the case where the document was deleted +<4> Handle the case where the document was not impacted by the update, +ie no operation (noop) was executed on the document + +When the source retrieval is enabled in the `UpdateRequest` +through the fetchSource method, the response contains the +source of the updated document: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-getresult] +-------------------------------------------------- +<1> Retrieve the updated document as a `GetResult` +<2> Retrieve the source of the updated document as a `String` +<3> Retrieve the source of the updated document as a `Map` +<4> Retrieve the source of the updated document as a `byte[]` +<5> Handle the scenario where the source of the document is not present in +the response (this is the case by default) + +It is also possible to check for shard failures: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-failure] +-------------------------------------------------- +<1> Handle the situation where number of successful shards is less than +total shards +<2> Handle the potential failures + +When a `UpdateRequest` is performed against a document that does not exist, +the response has `404` status code, an `ElasticsearchException` gets thrown +which needs to be handled as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-docnotfound] +-------------------------------------------------- +<1> Handle the exception thrown because the document not exist + +If there is a version conflict, an `ElasticsearchException` will +be thrown: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/CRUDDocumentationIT.java[update-conflict] +-------------------------------------------------- +<1> The raised exception indicates that a version conflict error was returned.