Add get mappings support to high-level rest client (#30889)
This adds support for the get mappings API to the high level rest client. Relates to #27205
This commit is contained in:
parent
f94a75778c
commit
b22a055bcf
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.client;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||
|
@ -38,6 +39,8 @@ import org.elasticsearch.action.admin.indices.flush.SyncedFlushRequest;
|
|||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
|
||||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
|
||||
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
|
||||
|
@ -139,6 +142,29 @@ public final class IndicesClient {
|
|||
PutMappingResponse::fromXContent, listener, emptySet(), headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mappings on an index or indices using the Get Mapping API
|
||||
* <p>
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-mapping.html">
|
||||
* Get Mapping API on elastic.co</a>
|
||||
*/
|
||||
public GetMappingsResponse getMappings(GetMappingsRequest getMappingsRequest, Header... headers) throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(getMappingsRequest, RequestConverters::getMappings,
|
||||
GetMappingsResponse::fromXContent, emptySet(), headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves the mappings on an index on indices using the Get Mapping API
|
||||
* <p>
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-mapping.html">
|
||||
* Get Mapping API on elastic.co</a>
|
||||
*/
|
||||
public void getMappingsAsync(GetMappingsRequest getMappingsRequest, ActionListener<GetMappingsResponse> listener,
|
||||
Header... headers) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(getMappingsRequest, RequestConverters::getMappings,
|
||||
GetMappingsResponse::fromXContent, listener, emptySet(), headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates aliases using the Index Aliases API
|
||||
* <p>
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.elasticsearch.action.admin.indices.flush.FlushRequest;
|
|||
import org.elasticsearch.action.admin.indices.flush.SyncedFlushRequest;
|
||||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
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;
|
||||
|
@ -195,6 +196,19 @@ final class RequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
static Request getMappings(GetMappingsRequest getMappingsRequest) throws IOException {
|
||||
String[] indices = getMappingsRequest.indices() == null ? Strings.EMPTY_ARRAY : getMappingsRequest.indices();
|
||||
String[] types = getMappingsRequest.types() == null ? Strings.EMPTY_ARRAY : getMappingsRequest.types();
|
||||
|
||||
Request request = new Request(HttpGet.METHOD_NAME, endpoint(indices, "_mapping", types));
|
||||
|
||||
Params parameters = new Params(request);
|
||||
parameters.withMasterTimeout(getMappingsRequest.masterNodeTimeout());
|
||||
parameters.withIndicesOptions(getMappingsRequest.indicesOptions());
|
||||
parameters.withLocal(getMappingsRequest.local());
|
||||
return request;
|
||||
}
|
||||
|
||||
static Request refresh(RefreshRequest refreshRequest) {
|
||||
String[] indices = refreshRequest.indices() == null ? Strings.EMPTY_ARRAY : refreshRequest.indices();
|
||||
Request request = new Request(HttpPost.METHOD_NAME, endpoint(indices, "_refresh"));
|
||||
|
|
|
@ -42,6 +42,8 @@ import org.elasticsearch.action.admin.indices.flush.SyncedFlushRequest;
|
|||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
|
||||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
|
||||
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
|
||||
|
@ -79,6 +81,7 @@ import org.elasticsearch.rest.RestStatus;
|
|||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
|
||||
|
@ -328,6 +331,42 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testGetMapping() throws IOException {
|
||||
String indexName = "test";
|
||||
createIndex(indexName, Settings.EMPTY);
|
||||
|
||||
PutMappingRequest putMappingRequest = new PutMappingRequest(indexName);
|
||||
putMappingRequest.type("_doc");
|
||||
XContentBuilder mappingBuilder = JsonXContent.contentBuilder();
|
||||
mappingBuilder.startObject().startObject("properties").startObject("field");
|
||||
mappingBuilder.field("type", "text");
|
||||
mappingBuilder.endObject().endObject().endObject();
|
||||
putMappingRequest.source(mappingBuilder);
|
||||
|
||||
PutMappingResponse putMappingResponse =
|
||||
execute(putMappingRequest, highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync);
|
||||
assertTrue(putMappingResponse.isAcknowledged());
|
||||
|
||||
Map<String, Object> getIndexResponse = getAsMap(indexName);
|
||||
assertEquals("text", XContentMapValues.extractValue(indexName + ".mappings._doc.properties.field.type", getIndexResponse));
|
||||
|
||||
GetMappingsRequest request = new GetMappingsRequest()
|
||||
.indices(indexName)
|
||||
.types("_doc");
|
||||
|
||||
GetMappingsResponse getMappingsResponse =
|
||||
execute(request, highLevelClient().indices()::getMappings, highLevelClient().indices()::getMappingsAsync);
|
||||
|
||||
Map<String, Object> mappings = getMappingsResponse.getMappings().get(indexName).get("_doc").sourceAsMap();
|
||||
Map<String, String> type = new HashMap<>();
|
||||
type.put("type", "text");
|
||||
Map<String, Object> field = new HashMap<>();
|
||||
field.put("field", type);
|
||||
Map<String, Object> expected = new HashMap<>();
|
||||
expected.put("properties", field);
|
||||
assertThat(mappings, equalTo(expected));
|
||||
}
|
||||
|
||||
public void testDeleteIndex() throws IOException {
|
||||
{
|
||||
// Delete index if exists
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.elasticsearch.action.admin.indices.flush.FlushRequest;
|
|||
import org.elasticsearch.action.admin.indices.flush.SyncedFlushRequest;
|
||||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
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;
|
||||
|
@ -403,6 +404,47 @@ public class RequestConvertersTests extends ESTestCase {
|
|||
assertToXContentBody(putMappingRequest, request.getEntity());
|
||||
}
|
||||
|
||||
public void testGetMapping() throws IOException {
|
||||
GetMappingsRequest getMappingRequest = new GetMappingsRequest();
|
||||
|
||||
String[] indices = Strings.EMPTY_ARRAY;
|
||||
if (randomBoolean()) {
|
||||
indices = randomIndicesNames(0, 5);
|
||||
getMappingRequest.indices(indices);
|
||||
} else if (randomBoolean()) {
|
||||
getMappingRequest.indices((String[]) null);
|
||||
}
|
||||
|
||||
String type = null;
|
||||
if (randomBoolean()) {
|
||||
type = randomAlphaOfLengthBetween(3, 10);
|
||||
getMappingRequest.types(type);
|
||||
} else if (randomBoolean()) {
|
||||
getMappingRequest.types((String[]) null);
|
||||
}
|
||||
|
||||
Map<String, String> expectedParams = new HashMap<>();
|
||||
|
||||
setRandomIndicesOptions(getMappingRequest::indicesOptions, getMappingRequest::indicesOptions, expectedParams);
|
||||
setRandomMasterTimeout(getMappingRequest, expectedParams);
|
||||
setRandomLocal(getMappingRequest, expectedParams);
|
||||
|
||||
Request request = RequestConverters.getMappings(getMappingRequest);
|
||||
StringJoiner endpoint = new StringJoiner("/", "/", "");
|
||||
String index = String.join(",", indices);
|
||||
if (Strings.hasLength(index)) {
|
||||
endpoint.add(index);
|
||||
}
|
||||
endpoint.add("_mapping");
|
||||
if (type != null) {
|
||||
endpoint.add(type);
|
||||
}
|
||||
assertThat(endpoint.toString(), equalTo(request.getEndpoint()));
|
||||
|
||||
assertThat(expectedParams, equalTo(request.getParameters()));
|
||||
assertThat(HttpGet.METHOD_NAME, equalTo(request.getMethod()));
|
||||
}
|
||||
|
||||
public void testDeleteIndex() {
|
||||
String[] indices = randomIndicesNames(0, 5);
|
||||
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indices);
|
||||
|
|
|
@ -41,6 +41,8 @@ import org.elasticsearch.action.admin.indices.flush.SyncedFlushRequest;
|
|||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
|
||||
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
|
||||
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
|
||||
|
@ -64,6 +66,8 @@ import org.elasticsearch.action.support.IndicesOptions;
|
|||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.client.SyncedFlushResponse;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||
|
@ -81,6 +85,8 @@ import java.util.Map;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
* This class is used to generate the Java Indices API documentation.
|
||||
* You need to wrap your code between two tags like:
|
||||
|
@ -557,6 +563,133 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
|
|||
}
|
||||
}
|
||||
|
||||
public void testGetMapping() throws IOException {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
|
||||
{
|
||||
CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter"));
|
||||
assertTrue(createIndexResponse.isAcknowledged());
|
||||
PutMappingRequest request = new PutMappingRequest("twitter");
|
||||
request.type("tweet");
|
||||
request.source(
|
||||
"{\n" +
|
||||
" \"properties\": {\n" +
|
||||
" \"message\": {\n" +
|
||||
" \"type\": \"text\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}", // <1>
|
||||
XContentType.JSON);
|
||||
PutMappingResponse putMappingResponse = client.indices().putMapping(request);
|
||||
assertTrue(putMappingResponse.isAcknowledged());
|
||||
}
|
||||
|
||||
{
|
||||
// tag::get-mapping-request
|
||||
GetMappingsRequest request = new GetMappingsRequest(); // <1>
|
||||
request.indices("twitter"); // <2>
|
||||
request.types("tweet"); // <3>
|
||||
// end::get-mapping-request
|
||||
|
||||
// tag::get-mapping-request-masterTimeout
|
||||
request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
|
||||
request.masterNodeTimeout("1m"); // <2>
|
||||
// end::get-mapping-request-masterTimeout
|
||||
|
||||
// tag::get-mapping-request-indicesOptions
|
||||
request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1>
|
||||
// end::get-mapping-request-indicesOptions
|
||||
|
||||
// tag::get-mapping-execute
|
||||
GetMappingsResponse getMappingResponse = client.indices().getMappings(request);
|
||||
// end::get-mapping-execute
|
||||
|
||||
// tag::get-mapping-response
|
||||
ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> allMappings = getMappingResponse.mappings(); // <1>
|
||||
MappingMetaData typeMapping = allMappings.get("twitter").get("tweet"); // <2>
|
||||
Map<String, Object> tweetMapping = typeMapping.sourceAsMap(); // <3>
|
||||
// end::get-mapping-response
|
||||
|
||||
Map<String, String> type = new HashMap<>();
|
||||
type.put("type", "text");
|
||||
Map<String, Object> field = new HashMap<>();
|
||||
field.put("message", type);
|
||||
Map<String, Object> expected = new HashMap<>();
|
||||
expected.put("properties", field);
|
||||
assertThat(tweetMapping, equalTo(expected));
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetMappingAsync() throws Exception {
|
||||
final RestHighLevelClient client = highLevelClient();
|
||||
|
||||
{
|
||||
CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter"));
|
||||
assertTrue(createIndexResponse.isAcknowledged());
|
||||
PutMappingRequest request = new PutMappingRequest("twitter");
|
||||
request.type("tweet");
|
||||
request.source(
|
||||
"{\n" +
|
||||
" \"properties\": {\n" +
|
||||
" \"message\": {\n" +
|
||||
" \"type\": \"text\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}", // <1>
|
||||
XContentType.JSON);
|
||||
PutMappingResponse putMappingResponse = client.indices().putMapping(request);
|
||||
assertTrue(putMappingResponse.isAcknowledged());
|
||||
}
|
||||
|
||||
{
|
||||
GetMappingsRequest request = new GetMappingsRequest();
|
||||
request.indices("twitter");
|
||||
request.types("tweet");
|
||||
|
||||
// tag::get-mapping-execute-listener
|
||||
ActionListener<GetMappingsResponse> listener =
|
||||
new ActionListener<GetMappingsResponse>() {
|
||||
@Override
|
||||
public void onResponse(GetMappingsResponse putMappingResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
// end::get-mapping-execute-listener
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final ActionListener<GetMappingsResponse> latchListener = new LatchedActionListener<>(listener, latch);
|
||||
listener = ActionListener.wrap(r -> {
|
||||
ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> allMappings = r.mappings();
|
||||
MappingMetaData typeMapping = allMappings.get("twitter").get("tweet");
|
||||
Map<String, Object> tweetMapping = typeMapping.sourceAsMap();
|
||||
|
||||
Map<String, String> type = new HashMap<>();
|
||||
type.put("type", "text");
|
||||
Map<String, Object> field = new HashMap<>();
|
||||
field.put("message", type);
|
||||
Map<String, Object> expected = new HashMap<>();
|
||||
expected.put("properties", field);
|
||||
assertThat(tweetMapping, equalTo(expected));
|
||||
latchListener.onResponse(r);
|
||||
}, e -> {
|
||||
latchListener.onFailure(e);
|
||||
fail("should not fail");
|
||||
});
|
||||
|
||||
// tag::get-mapping-execute-async
|
||||
client.indices().getMappingsAsync(request, listener); // <1>
|
||||
// end::get-mapping-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
public void testOpenIndex() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
[[java-rest-high-get-mappings]]
|
||||
=== Get Mappings API
|
||||
|
||||
[[java-rest-high-get-mappings-request]]
|
||||
==== Get Mappings Request
|
||||
|
||||
A `GetMappingsRequest` can have an optional list of indices and optional list of types:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-mapping-request]
|
||||
--------------------------------------------------
|
||||
<1> An empty request that will return all indices and types
|
||||
<2> Setting the indices to fetch mapping for
|
||||
<3> The types to be returned
|
||||
|
||||
==== Optional arguments
|
||||
The following arguments can also optionally be provided:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-mapping-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[get-mapping-request-indicesOptions]
|
||||
--------------------------------------------------
|
||||
<1> Options for expanding indices names
|
||||
|
||||
[[java-rest-high-get-mappings-sync]]
|
||||
==== Synchronous Execution
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-mapping-execute]
|
||||
--------------------------------------------------
|
||||
|
||||
[[java-rest-high-get-mapping-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
The asynchronous execution of a get mappings request requires both the
|
||||
`GetMappingsRequest` instance and an `ActionListener` instance to be passed to
|
||||
the asynchronous method:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-mapping-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `GetMappingsRequest` 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 `GetMappingsResponse` looks like:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-mapping-execute-listener]
|
||||
--------------------------------------------------
|
||||
<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-get-mapping-response]]
|
||||
==== Get Mappings Response
|
||||
|
||||
The returned `GetMappingsResponse` allows to retrieve information about the
|
||||
executed operation as follows:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-mapping-response]
|
||||
--------------------------------------------------
|
||||
<1> Returning all indices' mappings
|
||||
<2> Retrieving the mappings for a particular index and type
|
||||
<3> Getting the mappings for the "tweet" as a Java Map
|
|
@ -95,6 +95,7 @@ include::indices/clear_cache.asciidoc[]
|
|||
include::indices/force_merge.asciidoc[]
|
||||
include::indices/rollover.asciidoc[]
|
||||
include::indices/put_mapping.asciidoc[]
|
||||
include::indices/get_mappings.asciidoc[]
|
||||
include::indices/update_aliases.asciidoc[]
|
||||
include::indices/exists_alias.asciidoc[]
|
||||
include::indices/put_settings.asciidoc[]
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.elasticsearch.test.ESTestCase;
|
|||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -140,6 +141,42 @@ public class XContentParserTests extends ESTestCase {
|
|||
assertThat(map.size(), equalTo(0));
|
||||
}
|
||||
|
||||
public void testMap() throws IOException {
|
||||
String source = "{\"i\": {\"_doc\": {\"f1\": {\"type\": \"text\", \"analyzer\": \"english\"}, " +
|
||||
"\"f2\": {\"type\": \"object\", \"properties\": {\"sub1\": {\"type\": \"keyword\", \"foo\": 17}}}}}}";
|
||||
Map<String, Object> f1 = new HashMap<>();
|
||||
f1.put("type", "text");
|
||||
f1.put("analyzer", "english");
|
||||
|
||||
Map<String, Object> sub1 = new HashMap<>();
|
||||
sub1.put("type", "keyword");
|
||||
sub1.put("foo", 17);
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put("sub1", sub1);
|
||||
|
||||
Map<String, Object> f2 = new HashMap<>();
|
||||
f2.put("type", "object");
|
||||
f2.put("properties", properties);
|
||||
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put("f1", f1);
|
||||
doc.put("f2", f2);
|
||||
|
||||
Map<String, Object> expected = new HashMap<>();
|
||||
expected.put("_doc", doc);
|
||||
|
||||
Map<String, Object> i = new HashMap<>();
|
||||
i.put("i", expected);
|
||||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, source)) {
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assertThat(token, equalTo(XContentParser.Token.START_OBJECT));
|
||||
Map<String, Object> map = parser.map();
|
||||
assertThat(map, equalTo(i));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> readMapStrings(String source) throws IOException {
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, source)) {
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
"default" : "open",
|
||||
"description" : "Whether to expand wildcard expression to concrete indices that are open, closed or both."
|
||||
},
|
||||
"master_timeout": {
|
||||
"type" : "time",
|
||||
"description" : "Specify timeout for connection to master"
|
||||
},
|
||||
"local": {
|
||||
"type": "boolean",
|
||||
"description": "Return local information, do not retrieve the state from master node (default: false)"
|
||||
|
|
|
@ -20,15 +20,31 @@
|
|||
package org.elasticsearch.action.admin.indices.mapping.get;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class GetMappingsResponse extends ActionResponse {
|
||||
public class GetMappingsResponse extends ActionResponse implements ToXContentFragment {
|
||||
|
||||
private static final ParseField MAPPINGS = new ParseField("mappings");
|
||||
|
||||
private static final ObjectParser<GetMappingsResponse, Void> PARSER =
|
||||
new ObjectParser<GetMappingsResponse, Void>("get-mappings", false, GetMappingsResponse::new);
|
||||
|
||||
private ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappings = ImmutableOpenMap.of();
|
||||
|
||||
|
@ -77,4 +93,94 @@ public class GetMappingsResponse extends ActionResponse {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GetMappingsResponse fromXContent(XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() == null) {
|
||||
parser.nextToken();
|
||||
}
|
||||
assert parser.currentToken() == XContentParser.Token.START_OBJECT;
|
||||
Map<String, Object> parts = parser.map();
|
||||
|
||||
ImmutableOpenMap.Builder<String, ImmutableOpenMap<String, MappingMetaData>> builder = new ImmutableOpenMap.Builder<>();
|
||||
for (Map.Entry<String, Object> entry : parts.entrySet()) {
|
||||
final String indexName = entry.getKey();
|
||||
assert entry.getValue() instanceof Map : "expected a map as type mapping, but got: " + entry.getValue().getClass();
|
||||
final Map<String, Object> mapping = (Map<String, Object>) ((Map) entry.getValue()).get(MAPPINGS.getPreferredName());
|
||||
|
||||
ImmutableOpenMap.Builder<String, MappingMetaData> typeBuilder = new ImmutableOpenMap.Builder<>();
|
||||
for (Map.Entry<String, Object> typeEntry : mapping.entrySet()) {
|
||||
final String typeName = typeEntry.getKey();
|
||||
assert typeEntry.getValue() instanceof Map : "expected a map as inner type mapping, but got: " +
|
||||
typeEntry.getValue().getClass();
|
||||
final Map<String, Object> fieldMappings = (Map<String, Object>) typeEntry.getValue();
|
||||
MappingMetaData mmd = new MappingMetaData(typeName, fieldMappings);
|
||||
typeBuilder.put(typeName, mmd);
|
||||
}
|
||||
builder.put(indexName, typeBuilder.build());
|
||||
}
|
||||
|
||||
return new GetMappingsResponse(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return toXContent(builder, params, true);
|
||||
}
|
||||
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params, boolean includeTypeName) throws IOException {
|
||||
for (final ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexEntry : getMappings()) {
|
||||
builder.startObject(indexEntry.key);
|
||||
{
|
||||
if (includeTypeName == false) {
|
||||
MappingMetaData mappings = null;
|
||||
for (final ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
|
||||
if (typeEntry.key.equals("_default_") == false) {
|
||||
assert mappings == null;
|
||||
mappings = typeEntry.value;
|
||||
}
|
||||
}
|
||||
if (mappings == null) {
|
||||
// no mappings yet
|
||||
builder.startObject(MAPPINGS.getPreferredName()).endObject();
|
||||
} else {
|
||||
builder.field(MAPPINGS.getPreferredName(), mappings.sourceAsMap());
|
||||
}
|
||||
} else {
|
||||
builder.startObject(MAPPINGS.getPreferredName());
|
||||
{
|
||||
for (final ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
|
||||
builder.field(typeEntry.key, typeEntry.value.sourceAsMap());
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mappings.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GetMappingsResponse other = (GetMappingsResponse) obj;
|
||||
return this.mappings.equals(other.mappings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.elasticsearch.common.collect.ImmutableOpenMap;
|
|||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.indices.TypeMissingException;
|
||||
|
@ -83,6 +84,7 @@ public class RestGetMappingAction extends BaseRestHandler {
|
|||
final GetMappingsRequest getMappingsRequest = new GetMappingsRequest();
|
||||
getMappingsRequest.indices(indices).types(types);
|
||||
getMappingsRequest.indicesOptions(IndicesOptions.fromRequest(request, getMappingsRequest.indicesOptions()));
|
||||
getMappingsRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getMappingsRequest.masterNodeTimeout()));
|
||||
getMappingsRequest.local(request.paramAsBoolean("local", getMappingsRequest.local()));
|
||||
return channel -> client.admin().indices().getMappings(getMappingsRequest, new RestBuilderListener<GetMappingsResponse>(channel) {
|
||||
@Override
|
||||
|
@ -129,54 +131,17 @@ public class RestGetMappingAction extends BaseRestHandler {
|
|||
status = RestStatus.OK;
|
||||
} else {
|
||||
status = RestStatus.NOT_FOUND;
|
||||
final String message;
|
||||
if (difference.size() == 1) {
|
||||
message = String.format(Locale.ROOT, "type [%s] missing", toNamesString(difference.iterator().next()));
|
||||
} else {
|
||||
message = String.format(Locale.ROOT, "types [%s] missing", toNamesString(difference.toArray(new String[0])));
|
||||
}
|
||||
final String message = String.format(Locale.ROOT, "type" + (difference.size() == 1 ? "" : "s") +
|
||||
" [%s] missing", Strings.collectionToCommaDelimitedString(difference));
|
||||
builder.field("error", message);
|
||||
builder.field("status", status.getStatus());
|
||||
}
|
||||
response.toXContent(builder, ToXContent.EMPTY_PARAMS, includeTypeName);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
for (final ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexEntry : mappingsByIndex) {
|
||||
builder.startObject(indexEntry.key);
|
||||
{
|
||||
if (includeTypeName == false) {
|
||||
MappingMetaData mappings = null;
|
||||
for (final ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
|
||||
if (typeEntry.key.equals("_default_") == false) {
|
||||
assert mappings == null;
|
||||
mappings = typeEntry.value;
|
||||
}
|
||||
}
|
||||
if (mappings == null) {
|
||||
// no mappings yet
|
||||
builder.startObject("mappings").endObject();
|
||||
} else {
|
||||
builder.field("mappings", mappings.sourceAsMap());
|
||||
}
|
||||
} else {
|
||||
builder.startObject("mappings");
|
||||
{
|
||||
for (final ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
|
||||
builder.field(typeEntry.key, typeEntry.value.sourceAsMap());
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
return new BytesRestResponse(status, builder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String toNamesString(final String... names) {
|
||||
return Arrays.stream(names).collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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.mapping.get;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||
import org.elasticsearch.test.EqualsHashCodeTestUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GetMappingsResponseTests extends AbstractStreamableXContentTestCase<GetMappingsResponse> {
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void testCheckEqualsAndHashCode() {
|
||||
GetMappingsResponse resp = createTestInstance();
|
||||
EqualsHashCodeTestUtils.checkEqualsAndHashCode(resp, r -> new GetMappingsResponse(r.mappings()), GetMappingsResponseTests::mutate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetMappingsResponse doParseInstance(XContentParser parser) throws IOException {
|
||||
return GetMappingsResponse.fromXContent(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetMappingsResponse createBlankInstance() {
|
||||
return new GetMappingsResponse();
|
||||
}
|
||||
|
||||
private static GetMappingsResponse mutate(GetMappingsResponse original) throws IOException {
|
||||
ImmutableOpenMap.Builder<String, ImmutableOpenMap<String, MappingMetaData>> builder = ImmutableOpenMap.builder(original.mappings());
|
||||
String indexKey = original.mappings().keys().iterator().next().value;
|
||||
|
||||
ImmutableOpenMap.Builder<String, MappingMetaData> typeBuilder = ImmutableOpenMap.builder(original.mappings().get(indexKey));
|
||||
final String typeKey;
|
||||
Iterator<ObjectCursor<String>> iter = original.mappings().get(indexKey).keys().iterator();
|
||||
if (iter.hasNext()) {
|
||||
typeKey = iter.next().value;
|
||||
} else {
|
||||
typeKey = "new-type";
|
||||
}
|
||||
|
||||
typeBuilder.put(typeKey, new MappingMetaData("type-" + randomAlphaOfLength(6), randomFieldMapping()));
|
||||
|
||||
builder.put(indexKey, typeBuilder.build());
|
||||
return new GetMappingsResponse(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetMappingsResponse mutateInstance(GetMappingsResponse instance) throws IOException {
|
||||
return mutate(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetMappingsResponse createTestInstance() {
|
||||
// rarely have no types
|
||||
int typeCount = rarely() ? 0 : scaledRandomIntBetween(1, 3);
|
||||
List<MappingMetaData> typeMappings = new ArrayList<>(typeCount);
|
||||
|
||||
for (int i = 0; i < typeCount; i++) {
|
||||
Map<String, Object> mappings = new HashMap<>();
|
||||
if (rarely() == false) { // rarely have no fields
|
||||
mappings.put("field-" + i, randomFieldMapping());
|
||||
if (randomBoolean()) {
|
||||
mappings.put("field2-" + i, randomFieldMapping());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
MappingMetaData mmd = new MappingMetaData("type-" + randomAlphaOfLength(5), mappings);
|
||||
typeMappings.add(mmd);
|
||||
} catch (IOException e) {
|
||||
fail("shouldn't have failed " + e);
|
||||
}
|
||||
}
|
||||
ImmutableOpenMap.Builder<String, MappingMetaData> typeBuilder = ImmutableOpenMap.builder();
|
||||
typeMappings.forEach(mmd -> typeBuilder.put(mmd.type(), mmd));
|
||||
ImmutableOpenMap.Builder<String, ImmutableOpenMap<String, MappingMetaData>> indexBuilder = ImmutableOpenMap.builder();
|
||||
indexBuilder.put("index-" + randomAlphaOfLength(5), typeBuilder.build());
|
||||
GetMappingsResponse resp = new GetMappingsResponse(indexBuilder.build());
|
||||
logger.debug("--> created: {}", resp);
|
||||
return resp;
|
||||
}
|
||||
|
||||
// Not meant to be exhaustive
|
||||
private static Map<String, Object> randomFieldMapping() {
|
||||
Map<String, Object> mappings = new HashMap<>();
|
||||
if (randomBoolean()) {
|
||||
Map<String, Object> regularMapping = new HashMap<>();
|
||||
regularMapping.put("type", randomBoolean() ? "text" : "keyword");
|
||||
regularMapping.put("index", "analyzed");
|
||||
regularMapping.put("analyzer", "english");
|
||||
return regularMapping;
|
||||
} else if (randomBoolean()) {
|
||||
Map<String, Object> numberMapping = new HashMap<>();
|
||||
numberMapping.put("type", randomFrom("integer", "float", "long", "double"));
|
||||
numberMapping.put("index", Objects.toString(randomBoolean()));
|
||||
return numberMapping;
|
||||
} else if (randomBoolean()) {
|
||||
Map<String, Object> objMapping = new HashMap<>();
|
||||
objMapping.put("type", "object");
|
||||
objMapping.put("dynamic", "strict");
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
Map<String, Object> props1 = new HashMap<>();
|
||||
props1.put("type", randomFrom("text", "keyword"));
|
||||
props1.put("analyzer", "keyword");
|
||||
properties.put("subtext", props1);
|
||||
Map<String, Object> props2 = new HashMap<>();
|
||||
props2.put("type", "object");
|
||||
Map<String, Object> prop2properties = new HashMap<>();
|
||||
Map<String, Object> props3 = new HashMap<>();
|
||||
props3.put("type", "integer");
|
||||
props3.put("index", "false");
|
||||
prop2properties.put("subsubfield", props3);
|
||||
props2.put("properties", prop2properties);
|
||||
objMapping.put("properties", properties);
|
||||
return objMapping;
|
||||
} else {
|
||||
Map<String, Object> plainMapping = new HashMap<>();
|
||||
plainMapping.put("type", "keyword");
|
||||
return plainMapping;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue