diff options
author | Catalin Ursachi <catalin.ursachi@softwire.com> | 2017-12-07 10:39:59 +0000 |
---|---|---|
committer | Luca Cavanna <javanna@users.noreply.github.com> | 2017-12-07 11:39:59 +0100 |
commit | f823cea79c5fc2cd6375cde3782a2e355439c57a (patch) | |
tree | f0220fec7d06baa28d7c6ddf9e4d4420f220b6c8 | |
parent | 0b102f63721472d7de89c21c78c9210aed90e7de (diff) |
Added Create Index support to high-level REST client (#27351)
Relates to #27205
15 files changed, 634 insertions, 54 deletions
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java index 2cc1d4849d..c876731839 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java @@ -21,6 +21,8 @@ package org.elasticsearch.client; import org.apache.http.Header; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; @@ -29,13 +31,13 @@ import java.util.Collections; /** * A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Indices API. - * + * <p> * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html">Indices API on elastic.co</a> */ public final class IndicesClient { private final RestHighLevelClient restHighLevelClient; - public IndicesClient(RestHighLevelClient restHighLevelClient) { + IndicesClient(RestHighLevelClient restHighLevelClient) { this.restHighLevelClient = restHighLevelClient; } @@ -56,8 +58,32 @@ public final class IndicesClient { * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-delete-index.html"> * Delete Index API on elastic.co</a> */ - public void deleteIndexAsync(DeleteIndexRequest deleteIndexRequest, ActionListener<DeleteIndexResponse> listener, Header... headers) { + public void deleteIndexAsync(DeleteIndexRequest deleteIndexRequest, ActionListener<DeleteIndexResponse> listener, + Header... headers) { restHighLevelClient.performRequestAsyncAndParseEntity(deleteIndexRequest, Request::deleteIndex, DeleteIndexResponse::fromXContent, listener, Collections.emptySet(), headers); } + + /** + * Creates an index using the Create Index API + * <p> + * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html"> + * Create Index API on elastic.co</a> + */ + public CreateIndexResponse createIndex(CreateIndexRequest createIndexRequest, Header... headers) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(createIndexRequest, Request::createIndex, CreateIndexResponse::fromXContent, + Collections.emptySet(), headers); + } + + /** + * Asynchronously creates an index using the Create Index API + * <p> + * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html"> + * Create Index API on elastic.co</a> + */ + public void createIndexAsync(CreateIndexRequest createIndexRequest, ActionListener<CreateIndexResponse> listener, + Header... headers) { + restHighLevelClient.performRequestAsyncAndParseEntity(createIndexRequest, Request::createIndex, CreateIndexResponse::fromXContent, + listener, Collections.emptySet(), headers); + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java index 05ce54437a..a3544ddb89 100755 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java @@ -29,6 +29,7 @@ import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.delete.DeleteRequest; @@ -137,6 +138,19 @@ public final class Request { return new Request(HttpDelete.METHOD_NAME, endpoint, parameters.getParams(), null); } + static Request createIndex(CreateIndexRequest createIndexRequest) throws IOException { + String endpoint = endpoint(createIndexRequest.indices(), Strings.EMPTY_ARRAY, ""); + + Params parameters = Params.builder(); + parameters.withTimeout(createIndexRequest.timeout()); + parameters.withMasterTimeout(createIndexRequest.masterNodeTimeout()); + parameters.withWaitForActiveShards(createIndexRequest.waitForActiveShards()); + parameters.withUpdateAllTypes(createIndexRequest.updateAllTypes()); + + HttpEntity entity = createEntity(createIndexRequest, REQUEST_BODY_CONTENT_TYPE); + return new Request(HttpPut.METHOD_NAME, endpoint, parameters.getParams(), entity); + } + static Request info() { return new Request(HttpGet.METHOD_NAME, "/", Collections.emptyMap(), null); } @@ -534,6 +548,13 @@ public final class Request { return putParam("timeout", timeout); } + Params withUpdateAllTypes(boolean updateAllTypes) { + if (updateAllTypes) { + return putParam("update_all_types", Boolean.TRUE.toString()); + } + return this; + } + Params withVersion(long version) { if (version != Versions.MATCH_ANY) { return putParam("version", Long.toString(version)); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java index 4045e56528..0d6430b591 100755 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java @@ -20,14 +20,88 @@ package org.elasticsearch.client; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.admin.indices.alias.Alias; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.rest.RestStatus; import java.io.IOException; +import java.util.Map; + +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; public class IndicesClientIT extends ESRestHighLevelClientTestCase { + @SuppressWarnings("unchecked") + public void testCreateIndex() throws IOException { + { + // Create index + String indexName = "plain_index"; + assertFalse(indexExists(indexName)); + + CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); + + CreateIndexResponse createIndexResponse = + execute(createIndexRequest, highLevelClient().indices()::createIndex, highLevelClient().indices()::createIndexAsync); + assertTrue(createIndexResponse.isAcknowledged()); + + assertTrue(indexExists(indexName)); + } + { + // Create index with mappings, aliases and settings + String indexName = "rich_index"; + assertFalse(indexExists(indexName)); + + CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); + + Alias alias = new Alias("alias_name"); + alias.filter("{\"term\":{\"year\":2016}}"); + alias.routing("1"); + createIndexRequest.alias(alias); + + Settings.Builder settings = Settings.builder(); + settings.put(SETTING_NUMBER_OF_REPLICAS, 2); + createIndexRequest.settings(settings); + + XContentBuilder mappingBuilder = JsonXContent.contentBuilder(); + mappingBuilder.startObject().startObject("properties").startObject("field"); + mappingBuilder.field("type", "text"); + mappingBuilder.endObject().endObject().endObject(); + createIndexRequest.mapping("type_name", mappingBuilder); + + CreateIndexResponse createIndexResponse = + execute(createIndexRequest, highLevelClient().indices()::createIndex, highLevelClient().indices()::createIndexAsync); + assertTrue(createIndexResponse.isAcknowledged()); + + Map<String, Object> indexMetaData = getIndexMetadata(indexName); + + Map<String, Object> settingsData = (Map) indexMetaData.get("settings"); + Map<String, Object> indexSettings = (Map) settingsData.get("index"); + assertEquals("2", indexSettings.get("number_of_replicas")); + + Map<String, Object> aliasesData = (Map) indexMetaData.get("aliases"); + Map<String, Object> aliasData = (Map) aliasesData.get("alias_name"); + assertEquals("1", aliasData.get("index_routing")); + Map<String, Object> filter = (Map) aliasData.get("filter"); + Map<String, Object> term = (Map) filter.get("term"); + assertEquals(2016, term.get("year")); + + Map<String, Object> mappingsData = (Map) indexMetaData.get("mappings"); + Map<String, Object> typeData = (Map) mappingsData.get("type_name"); + Map<String, Object> properties = (Map) typeData.get("properties"); + Map<String, Object> field = (Map) properties.get("field"); + + assertEquals("text", field.get("type")); + } + } + public void testDeleteIndex() throws IOException { { // Delete index if exists @@ -65,4 +139,18 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase { return response.getStatusLine().getStatusCode() == 200; } + + @SuppressWarnings("unchecked") + private Map<String, Object> getIndexMetadata(String index) throws IOException { + Response response = client().performRequest("GET", index); + + XContentType entityContentType = XContentType.fromMediaTypeOrFormat(response.getEntity().getContentType().getValue()); + Map<String, Object> responseEntity = XContentHelper.convertToMap(entityContentType.xContent(), response.getEntity().getContent(), + false); + + Map<String, Object> indexMetaData = (Map) responseEntity.get(index); + assertNotNull(indexMetaData); + + return indexMetaData; + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java index f72a7cb4db..182de30fd1 100755 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java @@ -25,6 +25,7 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkShardRequest; @@ -36,6 +37,7 @@ import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchScrollRequest; import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.master.AcknowledgedRequest; @@ -253,6 +255,34 @@ public class RequestTests extends ESTestCase { assertEquals(method, request.getMethod()); } + public void testCreateIndex() throws IOException { + CreateIndexRequest createIndexRequest = new CreateIndexRequest(); + + String indexName = "index-" + randomAlphaOfLengthBetween(2, 5); + + createIndexRequest.index(indexName); + + Map<String, String> expectedParams = new HashMap<>(); + + setRandomTimeout(createIndexRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams); + setRandomMasterTimeout(createIndexRequest, expectedParams); + setRandomWaitForActiveShards(createIndexRequest::waitForActiveShards, expectedParams); + + if (randomBoolean()) { + boolean updateAllTypes = randomBoolean(); + createIndexRequest.updateAllTypes(updateAllTypes); + if (updateAllTypes) { + expectedParams.put("update_all_types", Boolean.TRUE.toString()); + } + } + + Request request = Request.createIndex(createIndexRequest); + assertEquals("/" + indexName, request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertEquals("PUT", request.getMethod()); + assertToXContentBody(createIndexRequest, request.getEntity()); + } + public void testDeleteIndex() throws IOException { DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(); @@ -407,11 +437,7 @@ public class RequestTests extends ESTestCase { expectedParams.put("refresh", refreshPolicy.getValue()); } } - if (randomBoolean()) { - int waitForActiveShards = randomIntBetween(0, 10); - updateRequest.waitForActiveShards(waitForActiveShards); - expectedParams.put("wait_for_active_shards", String.valueOf(waitForActiveShards)); - } + setRandomWaitForActiveShards(updateRequest::waitForActiveShards, expectedParams); if (randomBoolean()) { long version = randomLong(); updateRequest.version(version); @@ -1016,6 +1042,14 @@ public class RequestTests extends ESTestCase { } } + private static void setRandomWaitForActiveShards(Consumer<Integer> setter, Map<String, String> expectedParams) { + if (randomBoolean()) { + int waitForActiveShards = randomIntBetween(0, 10); + setter.accept(waitForActiveShards); + expectedParams.put("wait_for_active_shards", String.valueOf(waitForActiveShards)); + } + } + private static void setRandomRefreshPolicy(ReplicatedWriteRequest<?> request, Map<String, String> expectedParams) { if (randomBoolean()) { WriteRequest.RefreshPolicy refreshPolicy = randomFrom(WriteRequest.RefreshPolicy.values()); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java index e866fb92aa..372cc17d13 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java @@ -21,13 +21,18 @@ package org.elasticsearch.client.documentation; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.alias.Alias; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; +import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.ESRestHighLevelClientTestCase; -import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.RestStatus; import java.io.IOException; @@ -52,8 +57,8 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase RestHighLevelClient client = highLevelClient(); { - Response createIndexResponse = client().performRequest("PUT", "/posts"); - assertEquals(200, createIndexResponse.getStatusLine().getStatusCode()); + CreateIndexResponse createIndexResponse = client.indices().createIndex(new CreateIndexRequest("posts")); + assertTrue(createIndexResponse.isAcknowledged()); } { @@ -61,14 +66,26 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase DeleteIndexRequest request = new DeleteIndexRequest("posts"); // <1> // end::delete-index-request + // tag::delete-index-request-timeout + request.timeout(TimeValue.timeValueMinutes(2)); // <1> + request.timeout("2m"); // <2> + // end::delete-index-request-timeout + // tag::delete-index-request-masterTimeout + request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.masterNodeTimeout("1m"); // <2> + // end::delete-index-request-masterTimeout + // tag::delete-index-request-indicesOptions + request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1> + // end::delete-index-request-indicesOptions + // tag::delete-index-execute DeleteIndexResponse deleteIndexResponse = client.indices().deleteIndex(request); // end::delete-index-execute - assertTrue(deleteIndexResponse.isAcknowledged()); // tag::delete-index-response boolean acknowledged = deleteIndexResponse.isAcknowledged(); // <1> // end::delete-index-response + assertTrue(acknowledged); // tag::delete-index-execute-async client.indices().deleteIndexAsync(request, new ActionListener<DeleteIndexResponse>() { @@ -86,25 +103,10 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase } { - DeleteIndexRequest request = new DeleteIndexRequest("posts"); - // tag::delete-index-request-timeout - request.timeout(TimeValue.timeValueMinutes(2)); // <1> - request.timeout("2m"); // <2> - // end::delete-index-request-timeout - // tag::delete-index-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.timeout("1m"); // <2> - // end::delete-index-request-masterTimeout - // tag::delete-index-request-indicesOptions - request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1> - // end::delete-index-request-indicesOptions - } - - { // tag::delete-index-notfound try { DeleteIndexRequest request = new DeleteIndexRequest("does_not_exist"); - DeleteIndexResponse deleteIndexResponse = client.indices().deleteIndex(request); + client.indices().deleteIndex(request); } catch (ElasticsearchException exception) { if (exception.status() == RestStatus.NOT_FOUND) { // <1> @@ -113,4 +115,79 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase // end::delete-index-notfound } } + + public void testCreateIndex() throws IOException { + RestHighLevelClient client = highLevelClient(); + + { + // tag::create-index-request + CreateIndexRequest request = new CreateIndexRequest("twitter"); // <1> + // end::create-index-request + + // tag::create-index-request-settings + request.settings(Settings.builder() // <1> + .put("index.number_of_shards", 3) + .put("index.number_of_replicas", 2) + ); + // end::create-index-request-settings + + // tag::create-index-request-mappings + request.mapping("tweet", // <1> + " {\n" + + " \"tweet\": {\n" + + " \"properties\": {\n" + + " \"message\": {\n" + + " \"type\": \"text\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }", // <2> + XContentType.JSON); + // end::create-index-request-mappings + + // tag::create-index-request-aliases + request.alias( + new Alias("twitter_alias") // <1> + ); + // end::create-index-request-aliases + + // tag::create-index-request-timeout + request.timeout(TimeValue.timeValueMinutes(2)); // <1> + request.timeout("2m"); // <2> + // end::create-index-request-timeout + // tag::create-index-request-masterTimeout + request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.masterNodeTimeout("1m"); // <2> + // end::create-index-request-masterTimeout + // tag::create-index-request-waitForActiveShards + request.waitForActiveShards(2); // <1> + request.waitForActiveShards(ActiveShardCount.DEFAULT); // <2> + // end::create-index-request-waitForActiveShards + + // tag::create-index-execute + CreateIndexResponse createIndexResponse = client.indices().createIndex(request); + // end::create-index-execute + + // tag::create-index-response + boolean acknowledged = createIndexResponse.isAcknowledged(); // <1> + boolean shardsAcked = createIndexResponse.isShardsAcked(); // <2> + // end::create-index-response + assertTrue(acknowledged); + assertTrue(shardsAcked); + + // tag::create-index-execute-async + client.indices().createIndexAsync(request, new ActionListener<CreateIndexResponse>() { + @Override + public void onResponse(CreateIndexResponse createIndexResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }); + // end::create-index-execute-async + } + } } diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java b/core/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java index a9e4c77778..dc088f815b 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java @@ -21,10 +21,13 @@ package org.elasticsearch.action.admin.indices.alias; import org.elasticsearch.ElasticsearchGenerationException; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; @@ -33,11 +36,17 @@ import org.elasticsearch.index.query.QueryBuilder; import java.io.IOException; import java.util.Map; +import java.util.Objects; /** * Represents an alias, to be associated with an index */ -public class Alias implements Streamable { +public class Alias implements Streamable, ToXContentObject { + + private static final ParseField FILTER = new ParseField("filter"); + private static final ParseField ROUTING = new ParseField("routing"); + private static final ParseField INDEX_ROUTING = new ParseField("index_routing", "indexRouting", "index-routing"); + private static final ParseField SEARCH_ROUTING = new ParseField("search_routing", "searchRouting", "search-routing"); private String name; @@ -196,16 +205,16 @@ public class Alias implements Streamable { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } else if (token == XContentParser.Token.START_OBJECT) { - if ("filter".equals(currentFieldName)) { + if (FILTER.match(currentFieldName)) { Map<String, Object> filter = parser.mapOrdered(); alias.filter(filter); } } else if (token == XContentParser.Token.VALUE_STRING) { - if ("routing".equals(currentFieldName)) { + if (ROUTING.match(currentFieldName)) { alias.routing(parser.text()); - } else if ("index_routing".equals(currentFieldName) || "indexRouting".equals(currentFieldName) || "index-routing".equals(currentFieldName)) { + } else if (INDEX_ROUTING.match(currentFieldName)) { alias.indexRouting(parser.text()); - } else if ("search_routing".equals(currentFieldName) || "searchRouting".equals(currentFieldName) || "search-routing".equals(currentFieldName)) { + } else if (SEARCH_ROUTING.match(currentFieldName)) { alias.searchRouting(parser.text()); } } @@ -214,6 +223,29 @@ public class Alias implements Streamable { } @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(name); + + if (filter != null) { + builder.rawField(FILTER.getPreferredName(), new BytesArray(filter), XContentType.JSON); + } + + if (indexRouting != null && indexRouting.equals(searchRouting)) { + builder.field(ROUTING.getPreferredName(), indexRouting); + } else { + if (indexRouting != null) { + builder.field(INDEX_ROUTING.getPreferredName(), indexRouting); + } + if (searchRouting != null) { + builder.field(SEARCH_ROUTING.getPreferredName(), searchRouting); + } + } + + builder.endObject(); + return builder; + } + + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java b/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java index 2d320b094b..f628974834 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java @@ -30,6 +30,7 @@ import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.MapBuilder; @@ -37,6 +38,7 @@ 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.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; @@ -65,7 +67,11 @@ import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS; * @see org.elasticsearch.client.Requests#createIndexRequest(String) * @see CreateIndexResponse */ -public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest> implements IndicesRequest { +public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest> implements IndicesRequest, ToXContentObject { + + private static final ParseField MAPPINGS = new ParseField("mappings"); + private static final ParseField SETTINGS = new ParseField("settings"); + private static final ParseField ALIASES = new ParseField("aliases"); private String cause = ""; @@ -376,14 +382,14 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest> public CreateIndexRequest source(Map<String, ?> source) { for (Map.Entry<String, ?> entry : source.entrySet()) { String name = entry.getKey(); - if (name.equals("settings")) { + if (SETTINGS.match(name)) { settings((Map<String, Object>) entry.getValue()); - } else if (name.equals("mappings")) { + } else if (MAPPINGS.match(name)) { Map<String, Object> mappings = (Map<String, Object>) entry.getValue(); for (Map.Entry<String, Object> entry1 : mappings.entrySet()) { mapping(entry1.getKey(), (Map<String, Object>) entry1.getValue()); } - } else if (name.equals("aliases")) { + } else if (ALIASES.match(name)) { aliases((Map<String, Object>) entry.getValue()); } else { // maybe custom? @@ -520,4 +526,32 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest> out.writeBoolean(updateAllTypes); waitForActiveShards.writeTo(out); } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + + builder.startObject(SETTINGS.getPreferredName()); + settings.toXContent(builder, params); + builder.endObject(); + + builder.startObject(MAPPINGS.getPreferredName()); + for (Map.Entry<String, String> entry : mappings.entrySet()) { + builder.rawField(entry.getKey(), new BytesArray(entry.getValue()), XContentType.JSON); + } + builder.endObject(); + + builder.startObject(ALIASES.getPreferredName()); + for (Alias alias : aliases) { + alias.toXContent(builder, params); + } + builder.endObject(); + + for (Map.Entry<String, IndexMetaData.Custom> entry : customs.entrySet()) { + builder.field(entry.getKey(), entry.getValue(), params); + } + + builder.endObject(); + return builder; + } } diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexResponse.java b/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexResponse.java index b770c11c6a..5c07b4024e 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexResponse.java @@ -39,20 +39,17 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constru */ public class CreateIndexResponse extends AcknowledgedResponse implements ToXContentObject { - private static final String SHARDS_ACKNOWLEDGED = "shards_acknowledged"; - private static final String INDEX = "index"; - - private static final ParseField SHARDS_ACKNOWLEDGED_PARSER = new ParseField(SHARDS_ACKNOWLEDGED); - private static final ParseField INDEX_PARSER = new ParseField(INDEX); + private static final ParseField SHARDS_ACKNOWLEDGED = new ParseField("shards_acknowledged"); + private static final ParseField INDEX = new ParseField("index"); private static final ConstructingObjectParser<CreateIndexResponse, Void> PARSER = new ConstructingObjectParser<>("create_index", true, args -> new CreateIndexResponse((boolean) args[0], (boolean) args[1], (String) args[2])); static { declareAcknowledgedField(PARSER); - PARSER.declareField(constructorArg(), (parser, context) -> parser.booleanValue(), SHARDS_ACKNOWLEDGED_PARSER, + PARSER.declareField(constructorArg(), (parser, context) -> parser.booleanValue(), SHARDS_ACKNOWLEDGED, ObjectParser.ValueType.BOOLEAN); - PARSER.declareField(constructorArg(), (parser, context) -> parser.text(), INDEX_PARSER, ObjectParser.ValueType.STRING); + PARSER.declareField(constructorArg(), (parser, context) -> parser.text(), INDEX, ObjectParser.ValueType.STRING); } private boolean shardsAcked; @@ -102,8 +99,8 @@ public class CreateIndexResponse extends AcknowledgedResponse implements ToXCont } public void addCustomFields(XContentBuilder builder) throws IOException { - builder.field(SHARDS_ACKNOWLEDGED, isShardsAcked()); - builder.field(INDEX, index()); + builder.field(SHARDS_ACKNOWLEDGED.getPreferredName(), isShardsAcked()); + builder.field(INDEX.getPreferredName(), index()); } @Override diff --git a/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedResponse.java b/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedResponse.java index e446796472..3cce3d554f 100755 --- a/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedResponse.java +++ b/core/src/main/java/org/elasticsearch/action/support/master/AcknowledgedResponse.java @@ -37,11 +37,10 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constru */ public abstract class AcknowledgedResponse extends ActionResponse { - private static final String ACKNOWLEDGED = "acknowledged"; - private static final ParseField ACKNOWLEDGED_PARSER = new ParseField(ACKNOWLEDGED); + private static final ParseField ACKNOWLEDGED = new ParseField("acknowledged"); protected static <T extends AcknowledgedResponse> void declareAcknowledgedField(ConstructingObjectParser<T, Void> PARSER) { - PARSER.declareField(constructorArg(), (parser, context) -> parser.booleanValue(), ACKNOWLEDGED_PARSER, + PARSER.declareField(constructorArg(), (parser, context) -> parser.booleanValue(), ACKNOWLEDGED, ObjectParser.ValueType.BOOLEAN); } @@ -78,6 +77,6 @@ public abstract class AcknowledgedResponse extends ActionResponse { } protected void addAcknowledgedField(XContentBuilder builder) throws IOException { - builder.field(ACKNOWLEDGED, isAcknowledged()); + builder.field(ACKNOWLEDGED.getPreferredName(), isAcknowledged()); } } diff --git a/core/src/main/java/org/elasticsearch/common/settings/Settings.java b/core/src/main/java/org/elasticsearch/common/settings/Settings.java index 3648abb78c..0a0a01c3fe 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/Settings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/Settings.java @@ -624,7 +624,7 @@ public final class Settings implements ToXContentFragment { } /** - * Parsers the generated xconten from {@link Settings#toXContent(XContentBuilder, Params)} into a new Settings object. + * Parsers the generated xcontent from {@link Settings#toXContent(XContentBuilder, Params)} into a new Settings object. * Note this method requires the parser to either be positioned on a null token or on * {@link org.elasticsearch.common.xcontent.XContentParser.Token#START_OBJECT}. */ diff --git a/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java b/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java index 4acdfd636b..41691f70c0 100644 --- a/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestTests.java @@ -20,13 +20,26 @@ package org.elasticsearch.action.admin.indices.create; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.action.admin.indices.alias.Alias; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.settings.Settings; +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 org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.ESTestCase; import java.io.IOException; +import java.util.Map; +import java.util.Set; + +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; +import static org.elasticsearch.common.xcontent.ToXContent.EMPTY_PARAMS; public class CreateIndexRequestTests extends ESTestCase { @@ -46,7 +59,7 @@ public class CreateIndexRequestTests extends ESTestCase { } } } - + public void testTopLevelKeys() throws IOException { String createIndex = "{\n" @@ -65,8 +78,168 @@ public class CreateIndexRequestTests extends ESTestCase { + "}"; CreateIndexRequest request = new CreateIndexRequest(); - ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, + ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> {request.source(createIndex, XContentType.JSON);}); assertEquals("unknown key [FOO_SHOULD_BE_ILLEGAL_HERE] for create index", e.getMessage()); } + + public void testToXContent() throws IOException { + CreateIndexRequest request = new CreateIndexRequest("foo"); + + String mapping = JsonXContent.contentBuilder().startObject().startObject("type").endObject().endObject().string(); + request.mapping("my_type", mapping, XContentType.JSON); + + Alias alias = new Alias("test_alias"); + alias.routing("1"); + alias.filter("{\"term\":{\"year\":2016}}"); + request.alias(alias); + + Settings.Builder settings = Settings.builder(); + settings.put(SETTING_NUMBER_OF_SHARDS, 10); + request.settings(settings); + + String actualRequestBody = Strings.toString(request); + + String expectedRequestBody = "{\"settings\":{\"index\":{\"number_of_shards\":\"10\"}}," + + "\"mappings\":{\"my_type\":{\"type\":{}}}," + + "\"aliases\":{\"test_alias\":{\"filter\":{\"term\":{\"year\":2016}},\"routing\":\"1\"}}}"; + + assertEquals(expectedRequestBody, actualRequestBody); + } + + public void testToAndFromXContent() throws IOException { + + final CreateIndexRequest createIndexRequest = createTestItem(); + + boolean humanReadable = randomBoolean(); + final XContentType xContentType = randomFrom(XContentType.values()); + BytesReference originalBytes = toShuffledXContent(createIndexRequest, xContentType, EMPTY_PARAMS, humanReadable); + + CreateIndexRequest parsedCreateIndexRequest = new CreateIndexRequest(createIndexRequest.index()); + parsedCreateIndexRequest.source(originalBytes, xContentType); + + assertMappingsEqual(createIndexRequest.mappings(), parsedCreateIndexRequest.mappings()); + assertAliasesEqual(createIndexRequest.aliases(), parsedCreateIndexRequest.aliases()); + assertEquals(createIndexRequest.settings(), parsedCreateIndexRequest.settings()); + } + + private void assertMappingsEqual(Map<String, String> expected, Map<String, String> actual) throws IOException { + assertEquals(expected.keySet(), actual.keySet()); + + for (Map.Entry<String, String> expectedEntry : expected.entrySet()) { + String expectedValue = expectedEntry.getValue(); + String actualValue = actual.get(expectedEntry.getKey()); + XContentParser expectedJson = createParser(XContentType.JSON.xContent(), expectedValue); + XContentParser actualJson = createParser(XContentType.JSON.xContent(), actualValue); + assertEquals(expectedJson.mapOrdered(), actualJson.mapOrdered()); + } + } + + private static void assertAliasesEqual(Set<Alias> expected, Set<Alias> actual) throws IOException { + assertEquals(expected, actual); + + for (Alias expectedAlias : expected) { + for (Alias actualAlias : actual) { + if (expectedAlias.equals(actualAlias)) { + // As Alias#equals only looks at name, we check the equality of the other Alias parameters here. + assertEquals(expectedAlias.filter(), actualAlias.filter()); + assertEquals(expectedAlias.indexRouting(), actualAlias.indexRouting()); + assertEquals(expectedAlias.searchRouting(), actualAlias.searchRouting()); + } + } + } + } + + /** + * Returns a random {@link CreateIndexRequest}. + */ + private static CreateIndexRequest createTestItem() throws IOException { + String index = randomAlphaOfLength(5); + + CreateIndexRequest request = new CreateIndexRequest(index); + + int aliasesNo = randomIntBetween(0, 2); + for (int i = 0; i < aliasesNo; i++) { + request.alias(randomAlias()); + } + + if (randomBoolean()) { + String type = randomAlphaOfLength(5); + request.mapping(type, randomMapping(type)); + } + + if (randomBoolean()) { + request.settings(randomIndexSettings()); + } + + return request; + } + + private static Settings randomIndexSettings() { + Settings.Builder builder = Settings.builder(); + + if (randomBoolean()) { + int numberOfShards = randomIntBetween(1, 10); + builder.put(SETTING_NUMBER_OF_SHARDS, numberOfShards); + } + + if (randomBoolean()) { + int numberOfReplicas = randomIntBetween(1, 10); + builder.put(SETTING_NUMBER_OF_REPLICAS, numberOfReplicas); + } + + return builder.build(); + } + + private static XContentBuilder randomMapping(String type) throws IOException { + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject().startObject(type); + + randomMappingFields(builder, true); + + builder.endObject().endObject(); + return builder; + } + + private static void randomMappingFields(XContentBuilder builder, boolean allowObjectField) throws IOException { + builder.startObject("properties"); + + int fieldsNo = randomIntBetween(0, 5); + for (int i = 0; i < fieldsNo; i++) { + builder.startObject(randomAlphaOfLength(5)); + + if (allowObjectField && randomBoolean()) { + randomMappingFields(builder, false); + } else { + builder.field("type", "text"); + } + + builder.endObject(); + } + + builder.endObject(); + } + + private static Alias randomAlias() { + Alias alias = new Alias(randomAlphaOfLength(5)); + + if (randomBoolean()) { + if (randomBoolean()) { + alias.routing(randomAlphaOfLength(5)); + } else { + if (randomBoolean()) { + alias.indexRouting(randomAlphaOfLength(5)); + } + if (randomBoolean()) { + alias.searchRouting(randomAlphaOfLength(5)); + } + } + } + + if (randomBoolean()) { + alias.filter("{\"term\":{\"year\":2016}}"); + } + + return alias; + } } diff --git a/docs/java-rest/high-level/apis/createindex.asciidoc b/docs/java-rest/high-level/apis/createindex.asciidoc new file mode 100644 index 0000000000..ebd9158e19 --- /dev/null +++ b/docs/java-rest/high-level/apis/createindex.asciidoc @@ -0,0 +1,97 @@ +[[java-rest-high-create-index]] +=== Create Index API + +[[java-rest-high-create-index-request]] +==== Create Index Request + +A `CreateIndexRequest` requires an `index` argument: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-request] +-------------------------------------------------- +<1> The index to create + +==== Index settings +Each index created can have specific settings associated with it. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-request-settings] +-------------------------------------------------- +<1> Settings for this index + +==== Index mappings +An index may be created with mappings for its document types + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-request-mappings] +-------------------------------------------------- +<1> The type to define +<2> The mapping for this type, provided as a JSON string + +==== Index aliases +Aliases can be set at index creation time + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-request-aliases] +-------------------------------------------------- +<1> The alias to define + +==== Optional arguments +The following arguments can optionally be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-request-timeout] +-------------------------------------------------- +<1> Timeout to wait for the all the nodes to acknowledge the index creation as a `TimeValue` +<2> Timeout to wait for the all the nodes to acknowledge the index creatiom as a `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-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[create-index-request-waitForActiveShards] +-------------------------------------------------- +<1> The number of active shard copies to wait for before proceeding with the operation, as an `int`. +<2> The number of active shard copies to wait for before proceeding with the operation, as an `ActiveShardCount`. + +[[java-rest-high-create-index-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-execute] +-------------------------------------------------- + +[[java-rest-high-create-index-async]] +==== Asynchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-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-create-index-response]] +==== Create Index Response + +The returned `CreateIndexResponse` allows to retrieve information about the executed + operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[create-index-response] +-------------------------------------------------- +<1> Indicates whether all of the nodes have acknowledged the request +<2> Indicates whether the requisite number of shard copies were started for each shard in the index before timing out diff --git a/docs/java-rest/high-level/apis/deleteindex.asciidoc b/docs/java-rest/high-level/apis/deleteindex.asciidoc index 3c0627de49..e256790cf9 100644 --- a/docs/java-rest/high-level/apis/deleteindex.asciidoc +++ b/docs/java-rest/high-level/apis/deleteindex.asciidoc @@ -65,7 +65,7 @@ The returned `DeleteIndexResponse` allows to retrieve information about the exec -------------------------------------------------- include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[delete-index-response] -------------------------------------------------- -<1> Indicates whether all of the nodes have acknowledged the request or not +<1> Indicates whether all of the nodes have acknowledged the request If the index was not found, an `ElasticsearchException` will be thrown: diff --git a/docs/java-rest/high-level/apis/index.asciidoc b/docs/java-rest/high-level/apis/index.asciidoc index 993951b5ae..2312f28372 100644 --- a/docs/java-rest/high-level/apis/index.asciidoc +++ b/docs/java-rest/high-level/apis/index.asciidoc @@ -1,3 +1,4 @@ +include::createindex.asciidoc[] include::deleteindex.asciidoc[] include::_index.asciidoc[] include::get.asciidoc[] diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 9e902e1715..7a6b55619f 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -4,6 +4,7 @@ The Java High Level REST Client supports the following APIs: Indices APIs:: +* <<java-rest-high-create-index>> * <<java-rest-high-delete-index>> Single document APIs:: |