REST high-level client: add support for Indices Update Settings API [take 2] (#29327)

Relates to #27205
This commit is contained in:
olcbean 2018-04-16 21:39:11 +02:00 committed by Luca Cavanna
parent e3d954c6a5
commit b3e3b80f1b
20 changed files with 825 additions and 26 deletions

View File

@ -139,7 +139,6 @@
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]broadcast[/\\]BroadcastOperationRequestBuilder.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]broadcast[/\\]TransportBroadcastAction.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]broadcast[/\\]node[/\\]TransportBroadcastByNodeAction.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]AcknowledgedRequest.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]AcknowledgedRequestBuilder.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]MasterNodeOperationRequestBuilder.java" checks="LineLength" />
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]action[/\\]support[/\\]master[/\\]MasterNodeReadOperationRequestBuilder.java" checks="LineLength" />

View File

@ -45,6 +45,8 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
@ -406,4 +408,28 @@ public final class IndicesClient {
restHighLevelClient.performRequestAsyncAndParseEntity(rolloverRequest, Request::rollover, RolloverResponse::fromXContent,
listener, emptySet(), headers);
}
/**
* Updates specific index level settings using the Update Indices Settings API
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html"> Update Indices Settings
* API on elastic.co</a>
*/
public UpdateSettingsResponse putSettings(UpdateSettingsRequest updateSettingsRequest, Header... headers) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(updateSettingsRequest, Request::indexPutSettings,
UpdateSettingsResponse::fromXContent, emptySet(), headers);
}
/**
* Asynchronously updates specific index level settings using the Update Indices Settings API
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html"> Update Indices Settings
* API on elastic.co</a>
*/
public void putSettingsAsync(UpdateSettingsRequest updateSettingsRequest, ActionListener<UpdateSettingsResponse> listener,
Header... headers) {
restHighLevelClient.performRequestAsyncAndParseEntity(updateSettingsRequest, Request::indexPutSettings,
UpdateSettingsResponse::fromXContent, listener, emptySet(), headers);
}
}

View File

@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.bulk.BulkRequest;
@ -593,7 +594,7 @@ public final class Request {
}
static Request indicesExist(GetIndexRequest request) {
//this can be called with no indices as argument by transport client, not via REST though
// this can be called with no indices as argument by transport client, not via REST though
if (request.indices() == null || request.indices().length == 0) {
throw new IllegalArgumentException("indices are mandatory");
}
@ -607,6 +608,20 @@ public final class Request {
return new Request(HttpHead.METHOD_NAME, endpoint, params.getParams(), null);
}
static Request indexPutSettings(UpdateSettingsRequest updateSettingsRequest) throws IOException {
Params parameters = Params.builder();
parameters.withTimeout(updateSettingsRequest.timeout());
parameters.withMasterTimeout(updateSettingsRequest.masterNodeTimeout());
parameters.withIndicesOptions(updateSettingsRequest.indicesOptions());
parameters.withFlatSettings(updateSettingsRequest.flatSettings());
parameters.withPreserveExisting(updateSettingsRequest.isPreserveExisting());
String[] indices = updateSettingsRequest.indices() == null ? Strings.EMPTY_ARRAY : updateSettingsRequest.indices();
String endpoint = endpoint(indices, "_settings");
HttpEntity entity = createEntity(updateSettingsRequest, REQUEST_BODY_CONTENT_TYPE);
return new Request(HttpPut.METHOD_NAME, endpoint, parameters.getParams(), entity);
}
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));
@ -824,6 +839,13 @@ public final class Request {
return this;
}
Params withPreserveExisting(boolean preserveExisting) {
if (preserveExisting) {
return putParam("preserve_existing", Boolean.TRUE.toString());
}
return this;
}
Map<String, String> getParams() {
return Collections.unmodifiableMap(params);
}

View File

@ -49,6 +49,8 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
@ -56,6 +58,8 @@ import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.broadcast.BroadcastResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
@ -63,6 +67,7 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
@ -72,6 +77,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
public class IndicesClientIT extends ESRestHighLevelClientTestCase {
@ -609,4 +615,97 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
assertEquals("test_new", rolloverResponse.getNewIndex());
}
}
public void testIndexPutSettings() throws IOException {
final Setting<Integer> dynamicSetting = IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING;
final String dynamicSettingKey = IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
final int dynamicSettingValue = 0;
final Setting<String> staticSetting = IndexSettings.INDEX_CHECK_ON_STARTUP;
final String staticSettingKey = IndexSettings.INDEX_CHECK_ON_STARTUP.getKey();
final String staticSettingValue = "true";
final Setting<Integer> unmodifiableSetting = IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING;
final String unmodifiableSettingKey = IndexMetaData.SETTING_NUMBER_OF_SHARDS;
final int unmodifiableSettingValue = 3;
String index = "index";
createIndex(index, Settings.EMPTY);
assertThat(dynamicSetting.getDefault(Settings.EMPTY), not(dynamicSettingValue));
UpdateSettingsRequest dynamicSettingRequest = new UpdateSettingsRequest();
dynamicSettingRequest.settings(Settings.builder().put(dynamicSettingKey, dynamicSettingValue).build());
UpdateSettingsResponse response = execute(dynamicSettingRequest, highLevelClient().indices()::putSettings,
highLevelClient().indices()::putSettingsAsync);
assertTrue(response.isAcknowledged());
Map<String, Object> indexSettingsAsMap = getIndexSettingsAsMap(index);
assertThat(indexSettingsAsMap.get(dynamicSettingKey), equalTo(String.valueOf(dynamicSettingValue)));
assertThat(staticSetting.getDefault(Settings.EMPTY), not(staticSettingValue));
UpdateSettingsRequest staticSettingRequest = new UpdateSettingsRequest();
staticSettingRequest.settings(Settings.builder().put(staticSettingKey, staticSettingValue).build());
ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(staticSettingRequest,
highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
assertThat(exception.getMessage(),
startsWith("Elasticsearch exception [type=illegal_argument_exception, "
+ "reason=Can't update non dynamic settings [[index.shard.check_on_startup]] for open indices [[index/"));
indexSettingsAsMap = getIndexSettingsAsMap(index);
assertNull(indexSettingsAsMap.get(staticSettingKey));
closeIndex(index);
response = execute(staticSettingRequest, highLevelClient().indices()::putSettings,
highLevelClient().indices()::putSettingsAsync);
assertTrue(response.isAcknowledged());
openIndex(index);
indexSettingsAsMap = getIndexSettingsAsMap(index);
assertThat(indexSettingsAsMap.get(staticSettingKey), equalTo(staticSettingValue));
assertThat(unmodifiableSetting.getDefault(Settings.EMPTY), not(unmodifiableSettingValue));
UpdateSettingsRequest unmodifiableSettingRequest = new UpdateSettingsRequest();
unmodifiableSettingRequest.settings(Settings.builder().put(unmodifiableSettingKey, unmodifiableSettingValue).build());
exception = expectThrows(ElasticsearchException.class, () -> execute(unmodifiableSettingRequest,
highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
assertThat(exception.getMessage(), startsWith(
"Elasticsearch exception [type=illegal_argument_exception, "
+ "reason=Can't update non dynamic settings [[index.number_of_shards]] for open indices [[index/"));
closeIndex(index);
exception = expectThrows(ElasticsearchException.class, () -> execute(unmodifiableSettingRequest,
highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
assertThat(exception.getMessage(), startsWith(
"Elasticsearch exception [type=illegal_argument_exception, "
+ "reason=final index setting [index.number_of_shards], not updateable"));
}
@SuppressWarnings("unchecked")
private Map<String, Object> getIndexSettingsAsMap(String index) throws IOException {
Map<String, Object> indexSettings = getIndexSettings(index);
return (Map<String, Object>)((Map<String, Object>) indexSettings.get(index)).get("settings");
}
public void testIndexPutSettingNonExistent() throws IOException {
String index = "index";
UpdateSettingsRequest indexUpdateSettingsRequest = new UpdateSettingsRequest(index);
String setting = "no_idea_what_you_are_talking_about";
int value = 10;
indexUpdateSettingsRequest.settings(Settings.builder().put(setting, value).build());
ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(indexUpdateSettingsRequest,
highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
assertEquals(RestStatus.NOT_FOUND, exception.status());
assertThat(exception.getMessage(), equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]"));
createIndex(index, Settings.EMPTY);
exception = expectThrows(ElasticsearchException.class, () -> execute(indexUpdateSettingsRequest,
highLevelClient().indices()::putSettings, highLevelClient().indices()::putSettingsAsync));
assertThat(exception.status(), equalTo(RestStatus.BAD_REQUEST));
assertThat(exception.getMessage(), equalTo(
"Elasticsearch exception [type=illegal_argument_exception, "
+ "reason=unknown setting [index.no_idea_what_you_are_talking_about] please check that any required plugins are installed, "
+ "or check the breaking changes documentation for removed settings]"));
}
}

View File

@ -46,6 +46,7 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.action.bulk.BulkRequest;
@ -1339,6 +1340,33 @@ public class RequestTests extends ESTestCase {
assertEquals(expectedParams, request.getParameters());
}
public void testIndexPutSettings() throws IOException {
String[] indices = randomBoolean() ? null : randomIndicesNames(0, 2);
UpdateSettingsRequest updateSettingsRequest = new UpdateSettingsRequest(indices);
Map<String, String> expectedParams = new HashMap<>();
setRandomFlatSettings(updateSettingsRequest::flatSettings, expectedParams);
setRandomMasterTimeout(updateSettingsRequest, expectedParams);
setRandomTimeout(updateSettingsRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams);
setRandomIndicesOptions(updateSettingsRequest::indicesOptions, updateSettingsRequest::indicesOptions, expectedParams);
if (randomBoolean()) {
updateSettingsRequest.setPreserveExisting(randomBoolean());
if (updateSettingsRequest.isPreserveExisting()) {
expectedParams.put("preserve_existing", "true");
}
}
Request request = Request.indexPutSettings(updateSettingsRequest);
StringJoiner endpoint = new StringJoiner("/", "/", "");
if (indices != null && indices.length > 0) {
endpoint.add(String.join(",", indices));
}
endpoint.add("_settings");
assertThat(endpoint.toString(), equalTo(request.getEndpoint()));
assertEquals(HttpPut.METHOD_NAME, request.getMethod());
assertToXContentBody(updateSettingsRequest, request.getEntity());
assertEquals(expectedParams, request.getParameters());
}
private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException {
BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false);
assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());

View File

@ -48,6 +48,8 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
@ -56,6 +58,7 @@ import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
@ -394,6 +397,7 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
// tag::create-index-execute-listener
ActionListener<CreateIndexResponse> listener =
new ActionListener<CreateIndexResponse>() {
@Override
public void onResponse(CreateIndexResponse createIndexResponse) {
// <1>
@ -1378,4 +1382,110 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
public void testIndexPutSettings() throws Exception {
RestHighLevelClient client = highLevelClient();
{
CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("index"));
assertTrue(createIndexResponse.isAcknowledged());
}
// tag::put-settings-request
UpdateSettingsRequest request = new UpdateSettingsRequest("index1"); // <1>
UpdateSettingsRequest requestMultiple =
new UpdateSettingsRequest("index1", "index2"); // <2>
UpdateSettingsRequest requestAll = new UpdateSettingsRequest(); // <3>
// end::put-settings-request
// tag::put-settings-create-settings
String settingKey = "index.number_of_replicas";
int settingValue = 0;
Settings settings =
Settings.builder()
.put(settingKey, settingValue)
.build(); // <1>
// end::put-settings-create-settings
// tag::put-settings-request-index-settings
request.settings(settings);
// end::put-settings-request-index-settings
{
// tag::put-settings-settings-builder
Settings.Builder settingsBuilder =
Settings.builder()
.put(settingKey, settingValue);
request.settings(settingsBuilder); // <1>
// end::put-settings-settings-builder
}
{
// tag::put-settings-settings-map
Map<String, Object> map = new HashMap<>();
map.put(settingKey, settingValue);
request.settings(map); // <1>
// end::put-settings-settings-map
}
{
// tag::put-settings-settings-source
request.settings(
"{\"index.number_of_replicas\": \"2\"}"
, XContentType.JSON); // <1>
// end::put-settings-settings-source
}
// tag::put-settings-request-flat-settings
request.flatSettings(true); // <1>
// end::put-settings-request-flat-settings
// tag::put-settings-request-preserveExisting
request.setPreserveExisting(false); // <1>
// end::put-settings-request-preserveExisting
// tag::put-settings-request-timeout
request.timeout(TimeValue.timeValueMinutes(2)); // <1>
request.timeout("2m"); // <2>
// end::put-settings-request-timeout
// tag::put-settings-request-masterTimeout
request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
request.masterNodeTimeout("1m"); // <2>
// end::put-settings-request-masterTimeout
// tag::put-settings-request-indicesOptions
request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1>
// end::put-settings-request-indicesOptions
// tag::put-settings-execute
UpdateSettingsResponse updateSettingsResponse =
client.indices().putSettings(request);
// end::put-settings-execute
// tag::put-settings-response
boolean acknowledged = updateSettingsResponse.isAcknowledged(); // <1>
// end::put-settings-response
assertTrue(acknowledged);
// tag::put-settings-execute-listener
ActionListener<UpdateSettingsResponse> listener =
new ActionListener<UpdateSettingsResponse>() {
@Override
public void onResponse(UpdateSettingsResponse updateSettingsResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::put-settings-execute-listener
// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::put-settings-execute-async
client.indices().putSettingsAsync(request,listener); // <1>
// end::put-settings-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}

View File

@ -58,7 +58,7 @@ The following arguments can optionally be provided:
--------------------------------------------------
include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-request-flat-settings]
--------------------------------------------------
<1> Wether the updated settings returned in the `ClusterUpdateSettings` should
<1> Whether the updated settings returned in the `ClusterUpdateSettings` should
be in a flat format
["source","java",subs="attributes,callouts,macros"]

View File

@ -0,0 +1,142 @@
[[java-rest-high-indices-put-settings]]
=== Update Indices Settings API
The Update Indices Settings API allows to change specific index level settings.
[[java-rest-high-indices-put-settings-request]]
==== Update Indices Settings Request
An `UpdateSettingsRequest`:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request]
--------------------------------------------------
<1> Update settings for one index
<2> Update settings for multiple indices
<3> Update settings for all indices
==== Indices Settings
At least one setting to be updated must be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-create-settings]
--------------------------------------------------
<1> Sets the index settings to be applied
==== Providing the Settings
The settings to be applied can be provided in different ways:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-create-settings]
--------------------------------------------------
<1> Creates a setting as `Settings`
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-settings-builder]
--------------------------------------------------
<1> Settings provided as `Settings.Builder`
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-settings-source]
--------------------------------------------------
<1> Settings provided as `String`
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-settings-map]
--------------------------------------------------
<1> Settings provided as a `Map`
==== Optional Arguments
The following arguments can optionally be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-flat-settings]
--------------------------------------------------
<1> Whether the updated settings returned in the `UpdateSettings` should
be in a flat format
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-preserveExisting]
--------------------------------------------------
<1> Whether to update existing settings. If set to `true` existing settings
on an index remain unchanged, the default is `false`
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-timeout]
--------------------------------------------------
<1> Timeout to wait for the all the nodes to acknowledge the new setting
as a `TimeValue`
<2> Timeout to wait for the all the nodes to acknowledge the new setting
as a `String`
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-masterTimeout]
--------------------------------------------------
<1> Timeout to connect to the master node as a `TimeValue`
<2> Timeout to connect to the master node as a `String`
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-request-indicesOptions]
--------------------------------------------------
<1> Setting `IndicesOptions` controls how unavailable indices are resolved and
how wildcard expressions are expanded
[[java-rest-high-indices-put-settings-sync]]
==== Synchronous Execution
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-execute]
--------------------------------------------------
[[java-rest-high-indices-put-settings-async]]
==== Asynchronous Execution
The asynchronous execution of an indices update settings requires both the
`UpdateSettingsRequest` instance and an `ActionListener` instance to be
passed to the asynchronous method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-execute-async]
--------------------------------------------------
<1> The `UpdateSettingsRequest` to execute and the `ActionListener`
to use when the execution completes
The asynchronous method does not block and returns immediately. Once it is
completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for `UpdateSettingsResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of a failure. The raised exception is provided as an argument
[[java-rest-high-indices-put-settings-response]]
==== Update Indices Settings Response
The returned `UpdateSettingsResponse` allows to retrieve information about the
executed operation as follows:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-settings-response]
--------------------------------------------------
<1> Indicates whether all of the nodes have acknowledged the request

View File

@ -275,7 +275,7 @@ include-tagged::{doc-tests}/SearchDocumentationIT.java[search-execute-listener]
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
HTTP status code, execution time or whether the request terminated early or timed
out:
["source","java",subs="attributes,callouts,macros"]

View File

@ -64,6 +64,7 @@ Index Management::
* <<java-rest-high-clear-cache>>
* <<java-rest-high-force-merge>>
* <<java-rest-high-rollover-index>>
* <<java-rest-high-indices-put-settings>>
Mapping Management::
* <<java-rest-high-put-mapping>>
@ -87,6 +88,7 @@ include::indices/rollover.asciidoc[]
include::indices/put_mapping.asciidoc[]
include::indices/update_aliases.asciidoc[]
include::indices/exists_alias.asciidoc[]
include::indices/put_settings.asciidoc[]
== Cluster APIs

View File

@ -16,6 +16,10 @@
"type": "time",
"description": "Specify timeout for connection to master"
},
"timeout": {
"type" : "time",
"description" : "Explicit operation timeout"
},
"preserve_existing": {
"type": "boolean",
"description": "Whether to update existing settings. If set to `true` existing settings on an index remain unchanged, the default is `false`"
@ -34,10 +38,10 @@
"default": "open",
"description": "Whether to expand wildcard expression to concrete indices that are open, closed or both."
},
"flat_settings": {
"type": "boolean",
"description": "Return settings in flat format (default: false)"
}
"flat_settings": {
"type": "boolean",
"description": "Return settings in flat format (default: false)"
}
}
},
"body": {

View File

@ -28,27 +28,34 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.elasticsearch.action.ValidateActions.addValidationError;
import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS;
/**
* Request for an update index settings action
*/
public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsRequest> implements IndicesRequest.Replaceable {
public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsRequest>
implements IndicesRequest.Replaceable, ToXContentObject {
private String[] indices;
private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false, true, true);
private Settings settings = EMPTY_SETTINGS;
private boolean preserveExisting = false;
private boolean flatSettings = false;
public UpdateSettingsRequest() {
}
@ -68,6 +75,29 @@ public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsReq
this.settings = settings;
}
/**
* Sets the value of "flat_settings".
* Used only by the high-level REST client.
*
* @param flatSettings
* value of "flat_settings" flag to be set
* @return this request
*/
public UpdateSettingsRequest flatSettings(boolean flatSettings) {
this.flatSettings = flatSettings;
return this;
}
/**
* Return settings in flat format.
* Used only by the high-level REST client.
*
* @return <code>true</code> if settings need to be returned in flat format; <code>false</code> otherwise.
*/
public boolean flatSettings() {
return flatSettings;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
@ -178,4 +208,55 @@ public class UpdateSettingsRequest extends AcknowledgedRequest<UpdateSettingsReq
writeSettingsToStream(settings, out);
out.writeBoolean(preserveExisting);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
settings.toXContent(builder, params);
builder.endObject();
return builder;
}
public UpdateSettingsRequest fromXContent(XContentParser parser) throws IOException {
Map<String, Object> settings = new HashMap<>();
Map<String, Object> bodySettings = parser.map();
Object innerBodySettings = bodySettings.get("settings");
// clean up in case the body is wrapped with "settings" : { ... }
if (innerBodySettings instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> innerBodySettingsMap = (Map<String, Object>) innerBodySettings;
settings.putAll(innerBodySettingsMap);
} else {
settings.putAll(bodySettings);
}
return this.settings(settings);
}
@Override
public String toString() {
return "indices : " + Arrays.toString(indices) + "," + Strings.toString(this);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UpdateSettingsRequest that = (UpdateSettingsRequest) o;
return masterNodeTimeout.equals(that.masterNodeTimeout)
&& timeout.equals(that.timeout)
&& Objects.equals(settings, that.settings)
&& Objects.equals(indicesOptions, that.indicesOptions)
&& Objects.equals(preserveExisting, that.preserveExisting)
&& Arrays.equals(indices, that.indices);
}
@Override
public int hashCode() {
return Objects.hash(masterNodeTimeout, timeout, settings, indicesOptions, preserveExisting, Arrays.hashCode(indices));
}
}

View File

@ -22,6 +22,8 @@ package org.elasticsearch.action.admin.indices.settings.put;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
@ -30,6 +32,13 @@ import java.io.IOException;
*/
public class UpdateSettingsResponse extends AcknowledgedResponse {
private static final ConstructingObjectParser<UpdateSettingsResponse, Void> PARSER = new ConstructingObjectParser<>(
"update_index_settings", true, args -> new UpdateSettingsResponse((boolean) args[0]));
static {
declareAcknowledgedField(PARSER);
}
UpdateSettingsResponse() {
}
@ -48,4 +57,9 @@ public class UpdateSettingsResponse extends AcknowledgedResponse {
super.writeTo(out);
writeAcknowledged(out);
}
public static UpdateSettingsResponse fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
}

View File

@ -24,6 +24,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.unit.TimeValue;
import java.io.IOException;
import java.util.Objects;
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
@ -31,7 +32,8 @@ import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
* Abstract class that allows to mark action requests that support acknowledgements.
* Facilitates consistency across different api.
*/
public abstract class AcknowledgedRequest<Request extends MasterNodeRequest<Request>> extends MasterNodeRequest<Request> implements AckedRequest {
public abstract class AcknowledgedRequest<Request extends MasterNodeRequest<Request>> extends MasterNodeRequest<Request>
implements AckedRequest {
public static final TimeValue DEFAULT_ACK_TIMEOUT = timeValueSeconds(30);

View File

@ -25,6 +25,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.unit.TimeValue;
import java.io.IOException;
import java.util.Objects;
/**
* A based request for master based operation.
@ -76,4 +77,5 @@ public abstract class MasterNodeRequest<Request extends MasterNodeRequest<Reques
super.readFrom(in);
masterNodeTimeout = in.readTimeValue();
}
}

View File

@ -57,21 +57,7 @@ public class RestUpdateSettingsAction extends BaseRestHandler {
updateSettingsRequest.setPreserveExisting(request.paramAsBoolean("preserve_existing", updateSettingsRequest.isPreserveExisting()));
updateSettingsRequest.masterNodeTimeout(request.paramAsTime("master_timeout", updateSettingsRequest.masterNodeTimeout()));
updateSettingsRequest.indicesOptions(IndicesOptions.fromRequest(request, updateSettingsRequest.indicesOptions()));
Map<String, Object> settings = new HashMap<>();
try (XContentParser parser = request.contentParser()) {
Map<String, Object> bodySettings = parser.map();
Object innerBodySettings = bodySettings.get("settings");
// clean up in case the body is wrapped with "settings" : { ... }
if (innerBodySettings instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> innerBodySettingsMap = (Map<String, Object>) innerBodySettings;
settings.putAll(innerBodySettingsMap);
} else {
settings.putAll(bodySettings);
}
}
updateSettingsRequest.settings(settings);
updateSettingsRequest.fromXContent(request.contentParser());
return channel -> client.admin().indices().updateSettings(updateSettingsRequest, new RestToXContentListener<>(channel));
}

View File

@ -0,0 +1,133 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.settings.put;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.Settings.Builder;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.test.AbstractStreamableTestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.StringJoiner;
public class UpdateSettingsRequestStreamableTests extends AbstractStreamableTestCase<UpdateSettingsRequest> {
@Override
protected UpdateSettingsRequest mutateInstance(UpdateSettingsRequest request) {
UpdateSettingsRequest mutation = copyRequest(request);
List<Runnable> mutators = new ArrayList<>();
mutators.add(() -> mutation.masterNodeTimeout(randomTimeValue()));
mutators.add(() -> mutation.timeout(randomTimeValue()));
mutators.add(() -> mutation.settings(mutateSettings(request.settings())));
mutators.add(() -> mutation.indices(mutateIndices(request.indices())));
mutators.add(() -> mutation.indicesOptions(randomValueOtherThan(request.indicesOptions(),
() -> IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()))));
mutators.add(() -> mutation.setPreserveExisting(!request.isPreserveExisting()));
randomFrom(mutators).run();
return mutation;
}
@Override
protected UpdateSettingsRequest createTestInstance() {
return createTestItem();
}
@Override
protected UpdateSettingsRequest createBlankInstance() {
return new UpdateSettingsRequest();
}
public static UpdateSettingsRequest createTestItem() {
UpdateSettingsRequest request = randomBoolean()
? new UpdateSettingsRequest(randomSettings(0, 2))
: new UpdateSettingsRequest(randomSettings(0, 2), randomIndicesNames(0, 2));
request.masterNodeTimeout(randomTimeValue());
request.timeout(randomTimeValue());
request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()));
request.setPreserveExisting(randomBoolean());
request.flatSettings(randomBoolean());
return request;
}
private static UpdateSettingsRequest copyRequest(UpdateSettingsRequest request) {
UpdateSettingsRequest result = new UpdateSettingsRequest(request.settings(), request.indices());
result.masterNodeTimeout(request.timeout());
result.timeout(request.timeout());
result.indicesOptions(request.indicesOptions());
result.setPreserveExisting(request.isPreserveExisting());
result.flatSettings(request.flatSettings());
return result;
}
private static Settings mutateSettings(Settings settings) {
if (settings.isEmpty()) {
return randomSettings(1, 5);
}
Set<String> allKeys = settings.keySet();
List<String> keysToBeModified = randomSubsetOf(randomIntBetween(1, allKeys.size()), allKeys);
Builder builder = Settings.builder();
for (String key : allKeys) {
String value = settings.get(key);
if (keysToBeModified.contains(key)) {
value += randomAlphaOfLengthBetween(2, 5);
}
builder.put(key, value);
}
return builder.build();
}
private static String[] mutateIndices(String[] indices) {
if (CollectionUtils.isEmpty(indices)) {
return randomIndicesNames(1, 5);
}
String[] mutated = Arrays.copyOf(indices, indices.length);
Arrays.asList(mutated).replaceAll(i -> i += randomAlphaOfLengthBetween(2, 5));
return mutated;
}
private static Settings randomSettings(int min, int max) {
int num = randomIntBetween(min, max);
Builder builder = Settings.builder();
for (int i = 0; i < num; i++) {
int keyDepth = randomIntBetween(1, 5);
StringJoiner keyJoiner = new StringJoiner(".", "", "");
for (int d = 0; d < keyDepth; d++) {
keyJoiner.add(randomAlphaOfLengthBetween(3, 5));
}
builder.put(keyJoiner.toString(), randomAlphaOfLengthBetween(2, 5));
}
return builder.build();
}
private static String[] randomIndicesNames(int minIndicesNum, int maxIndicesNum) {
int numIndices = randomIntBetween(minIndicesNum, maxIndicesNum);
String[] indices = new String[numIndices];
for (int i = 0; i < numIndices; i++) {
indices[i] = "index-" + randomAlphaOfLengthBetween(2, 5).toLowerCase(Locale.ROOT);
}
return indices;
}
}

View File

@ -0,0 +1,87 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.settings.put;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
import java.io.IOException;
import java.util.function.Predicate;
public class UpdateSettingsRequestTests extends AbstractXContentTestCase<UpdateSettingsRequest> {
private final boolean enclosedSettings = randomBoolean();
@Override
protected UpdateSettingsRequest createTestInstance() {
UpdateSettingsRequest testRequest = UpdateSettingsRequestStreamableTests.createTestItem();
if (enclosedSettings) {
UpdateSettingsRequest requestWithEnclosingSettings = new UpdateSettingsRequest(testRequest.settings()) {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startObject("settings");
this.settings().toXContent(builder, params);
builder.endObject();
builder.endObject();
return builder;
}
};
return requestWithEnclosingSettings;
}
return testRequest;
}
@Override
protected UpdateSettingsRequest doParseInstance(XContentParser parser) throws IOException {
return new UpdateSettingsRequest().fromXContent(parser);
}
@Override
protected boolean supportsUnknownFields() {
// if the settings are enclose as a "settings" object
// then all other top-level elements will be ignored during the parsing
return enclosedSettings;
}
@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
if (enclosedSettings) {
return field -> field.startsWith("settings");
}
return field -> true;
}
@Override
protected void assertEqualInstances(UpdateSettingsRequest expectedInstance, UpdateSettingsRequest newInstance) {
// here only the settings should be tested, as this test covers explicitly only the XContent parsing
// the rest of the request fields are tested by the StreamableTests
super.assertEqualInstances(new UpdateSettingsRequest(expectedInstance.settings()),
new UpdateSettingsRequest(newInstance.settings()));
}
@Override
protected boolean assertToXContentEquivalence() {
// if enclosedSettings are used, disable the XContentEquivalence check as the
// parsed.toXContent is not equivalent to the test instance
return !enclosedSettings;
}
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.action.admin.indices.settings.put;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
public class UpdateSettingsResponseTests extends AbstractStreamableXContentTestCase<UpdateSettingsResponse> {
@Override
protected UpdateSettingsResponse doParseInstance(XContentParser parser) {
return UpdateSettingsResponse.fromXContent(parser);
}
@Override
protected UpdateSettingsResponse createTestInstance() {
return new UpdateSettingsResponse(randomBoolean());
}
@Override
protected UpdateSettingsResponse createBlankInstance() {
return new UpdateSettingsResponse();
}
@Override
protected UpdateSettingsResponse mutateInstance(UpdateSettingsResponse response) {
return new UpdateSettingsResponse(response.isAcknowledged() == false);
}
}

View File

@ -32,6 +32,7 @@ import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.ssl.SSLContexts;
import org.elasticsearch.core.internal.io.IOUtils;
import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
@ -491,6 +492,16 @@ public abstract class ESRestTestCase extends ESTestCase {
new StringEntity(Strings.toString(settings), ContentType.APPLICATION_JSON)));
}
protected static Map<String, Object> getIndexSettings(String index) throws IOException {
Map<String, String> params = new HashMap<>();
params.put("flat_settings", "true");
Response response = client().performRequest(HttpGet.METHOD_NAME, index + "/_settings", params);
assertOK(response);
try (InputStream is = response.getEntity().getContent()) {
return XContentHelper.convertToMap(XContentType.JSON.xContent(), is, true);
}
}
protected static boolean indexExists(String index) throws IOException {
Response response = client().performRequest(HttpHead.METHOD_NAME, index);
return RestStatus.OK.getStatus() == response.getStatusLine().getStatusCode();
@ -501,6 +512,11 @@ public abstract class ESRestTestCase extends ESTestCase {
assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
}
protected static void openIndex(String index) throws IOException {
Response response = client().performRequest(HttpPost.METHOD_NAME, index + "/_open");
assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
}
protected static boolean aliasExists(String alias) throws IOException {
Response response = client().performRequest(HttpHead.METHOD_NAME, "/_alias/" + alias);
return RestStatus.OK.getStatus() == response.getStatusLine().getStatusCode();