Skip to content

Commit 24c8d8f

Browse files
PnPiejavanna
authored andcommitted
REST high-level client: add force merge API (#28896)
Relates to #27205
1 parent 98f89c3 commit 24c8d8f

File tree

10 files changed

+341
-5
lines changed

10 files changed

+341
-5
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,17 @@
3434
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
3535
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
3636
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
37+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
38+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
3739
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
3840
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
3941
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
4042
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
4143
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
42-
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
43-
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
4444
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
4545
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
46+
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
47+
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
4648
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
4749
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
4850

@@ -261,6 +263,28 @@ public void flushAsync(FlushRequest flushRequest, ActionListener<FlushResponse>
261263
listener, emptySet(), headers);
262264
}
263265

266+
/**
267+
* Force merge one or more indices using the Force Merge API
268+
* <p>
269+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html">
270+
* Force Merge API on elastic.co</a>
271+
*/
272+
public ForceMergeResponse forceMerge(ForceMergeRequest forceMergeRequest, Header... headers) throws IOException {
273+
return restHighLevelClient.performRequestAndParseEntity(forceMergeRequest, Request::forceMerge, ForceMergeResponse::fromXContent,
274+
emptySet(), headers);
275+
}
276+
277+
/**
278+
* Asynchronously force merge one or more indices using the Force Merge API
279+
* <p>
280+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html">
281+
* Force Merge API on elastic.co</a>
282+
*/
283+
public void forceMergeAsync(ForceMergeRequest forceMergeRequest, ActionListener<ForceMergeResponse> listener, Header... headers) {
284+
restHighLevelClient.performRequestAsyncAndParseEntity(forceMergeRequest, Request::forceMerge, ForceMergeResponse::fromXContent,
285+
listener, emptySet(), headers);
286+
}
287+
264288
/**
265289
* Clears the cache of one or more indices using the Clear Cache API
266290
* <p>

client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
3838
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
3939
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
40+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
4041
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
4142
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
4243
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
@@ -233,6 +234,17 @@ static Request flush(FlushRequest flushRequest) {
233234
return new Request(HttpPost.METHOD_NAME, endpoint, parameters.getParams(), null);
234235
}
235236

237+
static Request forceMerge(ForceMergeRequest forceMergeRequest) {
238+
String[] indices = forceMergeRequest.indices() == null ? Strings.EMPTY_ARRAY : forceMergeRequest.indices();
239+
String endpoint = endpoint(indices, "_forcemerge");
240+
Params parameters = Params.builder();
241+
parameters.withIndicesOptions(forceMergeRequest.indicesOptions());
242+
parameters.putParam("max_num_segments", Integer.toString(forceMergeRequest.maxNumSegments()));
243+
parameters.putParam("only_expunge_deletes", Boolean.toString(forceMergeRequest.onlyExpungeDeletes()));
244+
parameters.putParam("flush", Boolean.toString(forceMergeRequest.flush()));
245+
return new Request(HttpPost.METHOD_NAME, endpoint, parameters.getParams(), null);
246+
}
247+
236248
static Request clearCache(ClearIndicesCacheRequest clearIndicesCacheRequest) {
237249
String[] indices = clearIndicesCacheRequest.indices() == null ? Strings.EMPTY_ARRAY :clearIndicesCacheRequest.indices();
238250
String endpoint = endpoint(indices, "_cache/clear");

client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
3939
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
4040
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
41+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
42+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
4143
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
4244
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
4345
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
@@ -467,6 +469,32 @@ public void testClearCache() throws IOException {
467469
}
468470
}
469471

472+
public void testForceMerge() throws IOException {
473+
{
474+
String index = "index";
475+
Settings settings = Settings.builder()
476+
.put("number_of_shards", 1)
477+
.put("number_of_replicas", 0)
478+
.build();
479+
createIndex(index, settings);
480+
ForceMergeRequest forceMergeRequest = new ForceMergeRequest(index);
481+
ForceMergeResponse forceMergeResponse =
482+
execute(forceMergeRequest, highLevelClient().indices()::forceMerge, highLevelClient().indices()::forceMergeAsync);
483+
assertThat(forceMergeResponse.getTotalShards(), equalTo(1));
484+
assertThat(forceMergeResponse.getSuccessfulShards(), equalTo(1));
485+
assertThat(forceMergeResponse.getFailedShards(), equalTo(0));
486+
assertThat(forceMergeResponse.getShardFailures(), equalTo(BroadcastResponse.EMPTY));
487+
}
488+
{
489+
String nonExistentIndex = "non_existent_index";
490+
assertFalse(indexExists(nonExistentIndex));
491+
ForceMergeRequest forceMergeRequest = new ForceMergeRequest(nonExistentIndex);
492+
ElasticsearchException exception = expectThrows(ElasticsearchException.class,
493+
() -> execute(forceMergeRequest, highLevelClient().indices()::forceMerge, highLevelClient().indices()::forceMergeAsync));
494+
assertEquals(RestStatus.NOT_FOUND, exception.status());
495+
}
496+
}
497+
470498
public void testExistsAlias() throws IOException {
471499
GetAliasesRequest getAliasesRequest = new GetAliasesRequest("alias");
472500
assertFalse(execute(getAliasesRequest, highLevelClient().indices()::existsAlias, highLevelClient().indices()::existsAliasAsync));

client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
4141
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
4242
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
43+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
4344
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
4445
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
4546
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
@@ -621,6 +622,43 @@ public void testFlush() {
621622
assertThat(request.getMethod(), equalTo(HttpPost.METHOD_NAME));
622623
}
623624

625+
public void testForceMerge() {
626+
String[] indices = randomBoolean() ? null : randomIndicesNames(0, 5);
627+
ForceMergeRequest forceMergeRequest;
628+
if (randomBoolean()) {
629+
forceMergeRequest = new ForceMergeRequest(indices);
630+
} else {
631+
forceMergeRequest = new ForceMergeRequest();
632+
forceMergeRequest.indices(indices);
633+
}
634+
635+
Map<String, String> expectedParams = new HashMap<>();
636+
setRandomIndicesOptions(forceMergeRequest::indicesOptions, forceMergeRequest::indicesOptions, expectedParams);
637+
if (randomBoolean()) {
638+
forceMergeRequest.maxNumSegments(randomInt());
639+
}
640+
expectedParams.put("max_num_segments", Integer.toString(forceMergeRequest.maxNumSegments()));
641+
if (randomBoolean()) {
642+
forceMergeRequest.onlyExpungeDeletes(randomBoolean());
643+
}
644+
expectedParams.put("only_expunge_deletes", Boolean.toString(forceMergeRequest.onlyExpungeDeletes()));
645+
if (randomBoolean()) {
646+
forceMergeRequest.flush(randomBoolean());
647+
}
648+
expectedParams.put("flush", Boolean.toString(forceMergeRequest.flush()));
649+
650+
Request request = Request.forceMerge(forceMergeRequest);
651+
StringJoiner endpoint = new StringJoiner("/", "/", "");
652+
if (indices != null && indices.length > 0) {
653+
endpoint.add(String.join(",", indices));
654+
}
655+
endpoint.add("_forcemerge");
656+
assertThat(request.getEndpoint(), equalTo(endpoint.toString()));
657+
assertThat(request.getParameters(), equalTo(expectedParams));
658+
assertThat(request.getEntity(), nullValue());
659+
assertThat(request.getMethod(), equalTo(HttpPost.METHOD_NAME));
660+
}
661+
624662
public void testClearCache() {
625663
String[] indices = randomBoolean() ? null : randomIndicesNames(0, 5);
626664
ClearIndicesCacheRequest clearIndicesCacheRequest;

client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
3838
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
3939
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
40+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
41+
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
4042
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
4143
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
4244
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
@@ -771,6 +773,79 @@ public void onFailure(Exception e) {
771773
}
772774
}
773775

776+
public void testForceMergeIndex() throws Exception {
777+
RestHighLevelClient client = highLevelClient();
778+
779+
{
780+
createIndex("index", Settings.EMPTY);
781+
}
782+
783+
{
784+
// tag::force-merge-request
785+
ForceMergeRequest request = new ForceMergeRequest("index1"); // <1>
786+
ForceMergeRequest requestMultiple = new ForceMergeRequest("index1", "index2"); // <2>
787+
ForceMergeRequest requestAll = new ForceMergeRequest(); // <3>
788+
// end::force-merge-request
789+
790+
// tag::force-merge-request-indicesOptions
791+
request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1>
792+
// end::force-merge-request-indicesOptions
793+
794+
// tag::force-merge-request-segments-num
795+
request.maxNumSegments(1); // <1>
796+
// end::force-merge-request-segments-num
797+
798+
// tag::force-merge-request-only-expunge-deletes
799+
request.onlyExpungeDeletes(true); // <1>
800+
// end::force-merge-request-only-expunge-deletes
801+
802+
// tag::force-merge-request-flush
803+
request.flush(true); // <1>
804+
// end::force-merge-request-flush
805+
806+
// tag::force-merge-execute
807+
ForceMergeResponse forceMergeResponse = client.indices().forceMerge(request);
808+
// end::force-merge-execute
809+
810+
// tag::force-merge-response
811+
int totalShards = forceMergeResponse.getTotalShards(); // <1>
812+
int successfulShards = forceMergeResponse.getSuccessfulShards(); // <2>
813+
int failedShards = forceMergeResponse.getFailedShards(); // <3>
814+
DefaultShardOperationFailedException[] failures = forceMergeResponse.getShardFailures(); // <4>
815+
// end::force-merge-response
816+
817+
// tag::force-merge-execute-listener
818+
ActionListener<ForceMergeResponse> listener = new ActionListener<ForceMergeResponse>() {
819+
@Override
820+
public void onResponse(ForceMergeResponse forceMergeResponse) {
821+
// <1>
822+
}
823+
824+
@Override
825+
public void onFailure(Exception e) {
826+
// <2>
827+
}
828+
};
829+
// end::force-merge-execute-listener
830+
831+
// tag::force-merge-execute-async
832+
client.indices().forceMergeAsync(request, listener); // <1>
833+
// end::force-merge-execute-async
834+
}
835+
{
836+
// tag::force-merge-notfound
837+
try {
838+
ForceMergeRequest request = new ForceMergeRequest("does_not_exist");
839+
client.indices().forceMerge(request);
840+
} catch (ElasticsearchException exception) {
841+
if (exception.status() == RestStatus.NOT_FOUND) {
842+
// <1>
843+
}
844+
}
845+
// end::force-merge-notfound
846+
}
847+
}
848+
774849
public void testClearCache() throws Exception {
775850
RestHighLevelClient client = highLevelClient();
776851

@@ -855,7 +930,6 @@ public void onFailure(Exception e) {
855930
}
856931
}
857932

858-
859933
public void testCloseIndex() throws Exception {
860934
RestHighLevelClient client = highLevelClient();
861935

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
[[java-rest-high-force-merge]]
2+
=== Force Merge API
3+
4+
[[java-rest-high-force-merge-request]]
5+
==== Force merge Request
6+
7+
A `ForceMergeRequest` can be applied to one or more indices, or even on `_all` the indices:
8+
9+
["source","java",subs="attributes,callouts,macros"]
10+
--------------------------------------------------
11+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-request]
12+
--------------------------------------------------
13+
<1> Force merge one index
14+
<2> Force merge multiple indices
15+
<3> Force merge all the indices
16+
17+
==== Optional arguments
18+
19+
["source","java",subs="attributes,callouts,macros"]
20+
--------------------------------------------------
21+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-request-indicesOptions]
22+
--------------------------------------------------
23+
<1> Setting `IndicesOptions` controls how unavailable indices are resolved and
24+
how wildcard expressions are expanded
25+
26+
["source","java",subs="attributes,callouts,macros"]
27+
--------------------------------------------------
28+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-request-segments-num]
29+
--------------------------------------------------
30+
<1> Set `max_num_segments` to control the number of segments to merge down to.
31+
32+
["source","java",subs="attributes,callouts,macros"]
33+
--------------------------------------------------
34+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-request-only-expunge-deletes]
35+
--------------------------------------------------
36+
<1> Set the `only_expunge_deletes` flag to `true`
37+
38+
["source","java",subs="attributes,callouts,macros"]
39+
--------------------------------------------------
40+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-request-flush]
41+
--------------------------------------------------
42+
<1> Set the `flush` flag to `true`
43+
44+
[[java-rest-high-force-merge-sync]]
45+
==== Synchronous Execution
46+
47+
["source","java",subs="attributes,callouts,macros"]
48+
--------------------------------------------------
49+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-execute]
50+
--------------------------------------------------
51+
52+
[[java-rest-high-force-merge-async]]
53+
==== Asynchronous Execution
54+
55+
The asynchronous execution of a force merge request requires both the `ForceMergeRequest`
56+
instance and an `ActionListener` instance to be passed to the asynchronous
57+
method:
58+
59+
["source","java",subs="attributes,callouts,macros"]
60+
--------------------------------------------------
61+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-execute-async]
62+
--------------------------------------------------
63+
<1> The `ForceMergeRequest` to execute and the `ActionListener` to use when
64+
the execution completes
65+
66+
The asynchronous method does not block and returns immediately. Once it is
67+
completed the `ActionListener` is called back using the `onResponse` method
68+
if the execution successfully completed or using the `onFailure` method if
69+
it failed.
70+
71+
A typical listener for `ForceMergeResponse` looks like:
72+
73+
["source","java",subs="attributes,callouts,macros"]
74+
--------------------------------------------------
75+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-execute-listener]
76+
--------------------------------------------------
77+
<1> Called when the execution is successfully completed. The response is
78+
provided as an argument
79+
<2> Called in case of failure. The raised exception is provided as an argument
80+
81+
[[java-rest-high-force-merge-response]]
82+
==== Force Merge Response
83+
84+
The returned `ForceMergeResponse` allows to retrieve information about the
85+
executed operation as follows:
86+
87+
["source","java",subs="attributes,callouts,macros"]
88+
--------------------------------------------------
89+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-response]
90+
--------------------------------------------------
91+
<1> Total number of shards hit by the force merge request
92+
<2> Number of shards where the force merge has succeeded
93+
<3> Number of shards where the force merge has failed
94+
<4> A list of failures if the operation failed on one or more shards
95+
96+
By default, if the indices were not found, an `ElasticsearchException` will be thrown:
97+
98+
["source","java",subs="attributes,callouts,macros"]
99+
--------------------------------------------------
100+
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[force-merge-notfound]
101+
--------------------------------------------------
102+
<1> Do something if the indices to be force merged were not found

docs/java-rest/high-level/supported-apis.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Index Management::
6060
* <<java-rest-high-refresh>>
6161
* <<java-rest-high-flush>>
6262
* <<java-rest-high-clear-cache>>
63+
* <<java-rest-high-force-merge>>
6364
* <<java-rest-high-rollover-index>>
6465

6566
Mapping Management::
@@ -79,6 +80,7 @@ include::indices/split_index.asciidoc[]
7980
include::indices/refresh.asciidoc[]
8081
include::indices/flush.asciidoc[]
8182
include::indices/clear_cache.asciidoc[]
83+
include::indices/force_merge.asciidoc[]
8284
include::indices/rollover.asciidoc[]
8385
include::indices/put_mapping.asciidoc[]
8486
include::indices/update_aliases.asciidoc[]

0 commit comments

Comments
 (0)