From c20cbe53e1d2443887e3b9d7f3a337b45d8eddd2 Mon Sep 17 00:00:00 2001 From: Catalin Ursachi Date: Thu, 7 Dec 2017 10:39:59 +0000 Subject: [PATCH] Added Create Index support to high-level REST client (#27351) Relates to #27205 --- .../elasticsearch/client/IndicesClient.java | 32 +++- .../org/elasticsearch/client/Request.java | 21 +++ .../elasticsearch/client/IndicesClientIT.java | 88 +++++++++ .../elasticsearch/client/RequestTests.java | 44 ++++- .../IndicesClientDocumentationIT.java | 117 ++++++++++-- .../action/admin/indices/alias/Alias.java | 42 ++++- .../indices/create/CreateIndexRequest.java | 42 ++++- .../indices/create/CreateIndexResponse.java | 15 +- .../support/master/AcknowledgedResponse.java | 7 +- .../common/settings/Settings.java | 2 +- .../create/CreateIndexRequestTests.java | 173 ++++++++++++++++++ .../high-level/apis/createindex.asciidoc | 97 ++++++++++ .../high-level/apis/deleteindex.asciidoc | 2 +- docs/java-rest/high-level/apis/index.asciidoc | 1 + .../high-level/supported-apis.asciidoc | 1 + 15 files changed, 632 insertions(+), 52 deletions(-) create mode 100644 docs/java-rest/high-level/apis/createindex.asciidoc 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 2cc1d4849d5a8..c876731839984 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 @@ 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 @@ /** * A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Indices API. - * + *

* See Indices API on elastic.co */ public final class IndicesClient { private final RestHighLevelClient restHighLevelClient; - public IndicesClient(RestHighLevelClient restHighLevelClient) { + IndicesClient(RestHighLevelClient restHighLevelClient) { this.restHighLevelClient = restHighLevelClient; } @@ -56,8 +58,32 @@ public DeleteIndexResponse deleteIndex(DeleteIndexRequest deleteIndexRequest, He * See * Delete Index API on elastic.co */ - public void deleteIndexAsync(DeleteIndexRequest deleteIndexRequest, ActionListener listener, Header... headers) { + public void deleteIndexAsync(DeleteIndexRequest deleteIndexRequest, ActionListener listener, + Header... headers) { restHighLevelClient.performRequestAsyncAndParseEntity(deleteIndexRequest, Request::deleteIndex, DeleteIndexResponse::fromXContent, listener, Collections.emptySet(), headers); } + + /** + * Creates an index using the Create Index API + *

+ * See + * Create Index API on elastic.co + */ + 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 + *

+ * See + * Create Index API on elastic.co + */ + public void createIndexAsync(CreateIndexRequest createIndexRequest, ActionListener 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 05ce54437a4d6..a3544ddb89b77 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.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 @@ static Request deleteIndex(DeleteIndexRequest deleteIndexRequest) { 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 @@ Params withTimeout(TimeValue timeout) { 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 4045e565288e5..0d6430b591294 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 indexMetaData = getIndexMetadata(indexName); + + Map settingsData = (Map) indexMetaData.get("settings"); + Map indexSettings = (Map) settingsData.get("index"); + assertEquals("2", indexSettings.get("number_of_replicas")); + + Map aliasesData = (Map) indexMetaData.get("aliases"); + Map aliasData = (Map) aliasesData.get("alias_name"); + assertEquals("1", aliasData.get("index_routing")); + Map filter = (Map) aliasData.get("filter"); + Map term = (Map) filter.get("term"); + assertEquals(2016, term.get("year")); + + Map mappingsData = (Map) indexMetaData.get("mappings"); + Map typeData = (Map) mappingsData.get("type_name"); + Map properties = (Map) typeData.get("properties"); + Map field = (Map) properties.get("field"); + + assertEquals("text", field.get("type")); + } + } + public void testDeleteIndex() throws IOException { { // Delete index if exists @@ -65,4 +139,18 @@ private static boolean indexExists(String index) throws IOException { return response.getStatusLine().getStatusCode() == 200; } + + @SuppressWarnings("unchecked") + private Map getIndexMetadata(String index) throws IOException { + Response response = client().performRequest("GET", index); + + XContentType entityContentType = XContentType.fromMediaTypeOrFormat(response.getEntity().getContentType().getValue()); + Map responseEntity = XContentHelper.convertToMap(entityContentType.xContent(), response.getEntity().getContent(), + false); + + Map 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 f72a7cb4dbf51..182de30fd1585 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.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.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 @@ private static void getAndExistsTest(Function requestConver assertEquals(method, request.getMethod()); } + public void testCreateIndex() throws IOException { + CreateIndexRequest createIndexRequest = new CreateIndexRequest(); + + String indexName = "index-" + randomAlphaOfLengthBetween(2, 5); + + createIndexRequest.index(indexName); + + Map 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 void testUpdate() throws IOException { 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 @@ private static void setRandomMasterTimeout(MasterNodeRequest request, Map setter, Map 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 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 e866fb92aae67..372cc17d137bc 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 @@ 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 void testDeleteIndex() throws IOException { 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 void testDeleteIndex() throws IOException { 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() { @@ -85,26 +102,11 @@ public void onFailure(Exception e) { // end::delete-index-execute-async } - { - 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 void onFailure(Exception e) { // 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() { + @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 a9e4c77778493..dc088f815b1c5 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 @@ 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 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 static Alias fromXContent(XContentParser parser) throws IOException { 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 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()); } } @@ -213,6 +222,29 @@ public static Alias fromXContent(XContentParser parser) throws IOException { return alias; } + @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; 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 eb0ede3c3f3e9..affe68a1b88e1 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.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.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 @@ * @see org.elasticsearch.client.Requests#createIndexRequest(String) * @see CreateIndexResponse */ -public class CreateIndexRequest extends AcknowledgedRequest implements IndicesRequest { +public class CreateIndexRequest extends AcknowledgedRequest 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 = ""; @@ -377,16 +383,16 @@ public CreateIndexRequest source(Map source) { boolean found = false; for (Map.Entry entry : source.entrySet()) { String name = entry.getKey(); - if (name.equals("settings")) { + if (SETTINGS.match(name)) { found = true; settings((Map) entry.getValue()); - } else if (name.equals("mappings")) { + } else if (MAPPINGS.match(name)) { found = true; Map mappings = (Map) entry.getValue(); for (Map.Entry entry1 : mappings.entrySet()) { mapping(entry1.getKey(), (Map) entry1.getValue()); } - } else if (name.equals("aliases")) { + } else if (ALIASES.match(name)) { found = true; aliases((Map) entry.getValue()); } else { @@ -526,4 +532,32 @@ public void writeTo(StreamOutput out) throws IOException { 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 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 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 b770c11c6ab03..5c07b4024ee7a 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 @@ */ 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 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 String index() { } 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 e4467964722c6..3cce3d554f07c 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 @@ */ 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 void declareAcknowledgedField(ConstructingObjectParser 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 @@ protected void writeAcknowledged(StreamOutput out) throws IOException { } 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 90db4a00c5cf8..9ff467ae27c45 100644 --- a/core/src/main/java/org/elasticsearch/common/settings/Settings.java +++ b/core/src/main/java/org/elasticsearch/common/settings/Settings.java @@ -654,7 +654,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } /** - * 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 74a87497181e5..c5e2aadccef99 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 @@ -19,13 +19,26 @@ package org.elasticsearch.action.admin.indices.create; +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 { @@ -45,4 +58,164 @@ public void testSerialization() throws IOException { } } } + + 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 expected, Map actual) throws IOException { + assertEquals(expected.keySet(), actual.keySet()); + + for (Map.Entry 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 expected, Set 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 0000000000000..ebd9158e19387 --- /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 3c0627de49a92..e256790cf9635 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 993951b5ae7f5..2312f28372060 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 9e902e1715766..7a6b55619f77a 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:: +* <> * <> Single document APIs::