Add get field mappings to High Level REST API Client (#31423)
Add get field mappings to High Level REST API Client Relates to #27205
This commit is contained in:
parent
b6cc6fc2bc
commit
b7ef75fed6
|
@ -37,6 +37,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.GetFieldMappingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse;
|
||||
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;
|
||||
|
@ -188,6 +190,35 @@ public final class IndicesClient {
|
|||
GetMappingsResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the field mappings on an index or indices using the Get Field Mapping API.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html">
|
||||
* Get Field Mapping API on elastic.co</a>
|
||||
* @param getFieldMappingsRequest the request
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response
|
||||
* @throws IOException in case there is a problem sending the request or parsing back the response
|
||||
*/
|
||||
public GetFieldMappingsResponse getFieldMapping(GetFieldMappingsRequest getFieldMappingsRequest,
|
||||
RequestOptions options) throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(getFieldMappingsRequest, RequestConverters::getFieldMapping, options,
|
||||
GetFieldMappingsResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves the field mappings on an index on indices using the Get Field Mapping API.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html">
|
||||
* Get Field Mapping API on elastic.co</a>
|
||||
* @param getFieldMappingsRequest the request
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @param listener the listener to be notified upon request completion
|
||||
*/
|
||||
public void getFieldMappingAsync(GetFieldMappingsRequest getFieldMappingsRequest, RequestOptions options,
|
||||
ActionListener<GetFieldMappingsResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(getFieldMappingsRequest, RequestConverters::getFieldMapping, options,
|
||||
GetFieldMappingsResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates aliases using the Index Aliases API.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html">
|
||||
|
|
|
@ -50,6 +50,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.GetFieldMappingsRequest;
|
||||
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;
|
||||
|
@ -230,6 +231,25 @@ final class RequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
static Request getFieldMapping(GetFieldMappingsRequest getFieldMappingsRequest) throws IOException {
|
||||
String[] indices = getFieldMappingsRequest.indices() == null ? Strings.EMPTY_ARRAY : getFieldMappingsRequest.indices();
|
||||
String[] types = getFieldMappingsRequest.types() == null ? Strings.EMPTY_ARRAY : getFieldMappingsRequest.types();
|
||||
String[] fields = getFieldMappingsRequest.fields() == null ? Strings.EMPTY_ARRAY : getFieldMappingsRequest.fields();
|
||||
|
||||
String endpoint = new EndpointBuilder().addCommaSeparatedPathParts(indices)
|
||||
.addPathPartAsIs("_mapping").addCommaSeparatedPathParts(types)
|
||||
.addPathPartAsIs("field").addCommaSeparatedPathParts(fields)
|
||||
.build();
|
||||
|
||||
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
|
||||
|
||||
Params parameters = new Params(request);
|
||||
parameters.withIndicesOptions(getFieldMappingsRequest.indicesOptions());
|
||||
parameters.withIncludeDefaults(getFieldMappingsRequest.includeDefaults());
|
||||
parameters.withLocal(getFieldMappingsRequest.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"));
|
||||
|
|
|
@ -43,6 +43,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.GetFieldMappingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse;
|
||||
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;
|
||||
|
@ -74,6 +76,7 @@ import org.elasticsearch.cluster.metadata.AliasMetaData;
|
|||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
|
||||
import org.elasticsearch.common.ValidationException;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||
|
@ -378,6 +381,41 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
|
|||
assertThat(mappings, equalTo(expected));
|
||||
}
|
||||
|
||||
public void testGetFieldMapping() 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());
|
||||
|
||||
GetFieldMappingsRequest getFieldMappingsRequest = new GetFieldMappingsRequest()
|
||||
.indices(indexName)
|
||||
.types("_doc")
|
||||
.fields("field");
|
||||
|
||||
GetFieldMappingsResponse getFieldMappingsResponse =
|
||||
execute(getFieldMappingsRequest,
|
||||
highLevelClient().indices()::getFieldMapping,
|
||||
highLevelClient().indices()::getFieldMappingAsync);
|
||||
|
||||
final Map<String, GetFieldMappingsResponse.FieldMappingMetaData> fieldMappingMap =
|
||||
getFieldMappingsResponse.mappings().get(indexName).get("_doc");
|
||||
|
||||
final GetFieldMappingsResponse.FieldMappingMetaData metaData =
|
||||
new GetFieldMappingsResponse.FieldMappingMetaData("field",
|
||||
new BytesArray("{\"field\":{\"type\":\"text\"}}"));
|
||||
assertThat(fieldMappingMap, equalTo(Collections.singletonMap("field", metaData)));
|
||||
}
|
||||
|
||||
public void testDeleteIndex() throws IOException {
|
||||
{
|
||||
// Delete index if exists
|
||||
|
|
|
@ -52,6 +52,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.GetFieldMappingsRequest;
|
||||
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;
|
||||
|
@ -457,6 +458,61 @@ public class RequestConvertersTests extends ESTestCase {
|
|||
assertThat(HttpGet.METHOD_NAME, equalTo(request.getMethod()));
|
||||
}
|
||||
|
||||
public void testGetFieldMapping() throws IOException {
|
||||
GetFieldMappingsRequest getFieldMappingsRequest = new GetFieldMappingsRequest();
|
||||
|
||||
String[] indices = Strings.EMPTY_ARRAY;
|
||||
if (randomBoolean()) {
|
||||
indices = randomIndicesNames(0, 5);
|
||||
getFieldMappingsRequest.indices(indices);
|
||||
} else if (randomBoolean()) {
|
||||
getFieldMappingsRequest.indices((String[]) null);
|
||||
}
|
||||
|
||||
String type = null;
|
||||
if (randomBoolean()) {
|
||||
type = randomAlphaOfLengthBetween(3, 10);
|
||||
getFieldMappingsRequest.types(type);
|
||||
} else if (randomBoolean()) {
|
||||
getFieldMappingsRequest.types((String[]) null);
|
||||
}
|
||||
|
||||
String[] fields = null;
|
||||
if (randomBoolean()) {
|
||||
fields = new String[randomIntBetween(1, 5)];
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
fields[i] = randomAlphaOfLengthBetween(3, 10);
|
||||
}
|
||||
getFieldMappingsRequest.fields(fields);
|
||||
} else if (randomBoolean()) {
|
||||
getFieldMappingsRequest.fields((String[]) null);
|
||||
}
|
||||
|
||||
Map<String, String> expectedParams = new HashMap<>();
|
||||
|
||||
setRandomIndicesOptions(getFieldMappingsRequest::indicesOptions, getFieldMappingsRequest::indicesOptions, expectedParams);
|
||||
setRandomLocal(getFieldMappingsRequest::local, expectedParams);
|
||||
|
||||
Request request = RequestConverters.getFieldMapping(getFieldMappingsRequest);
|
||||
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);
|
||||
}
|
||||
endpoint.add("field");
|
||||
if (fields != null) {
|
||||
endpoint.add(String.join(",", fields));
|
||||
}
|
||||
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);
|
||||
|
@ -2268,16 +2324,20 @@ public class RequestConvertersTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private static void setRandomLocal(MasterNodeReadRequest<?> request, Map<String, String> expectedParams) {
|
||||
private static void setRandomLocal(Consumer<Boolean> setter, Map<String, String> expectedParams) {
|
||||
if (randomBoolean()) {
|
||||
boolean local = randomBoolean();
|
||||
request.local(local);
|
||||
setter.accept(local);
|
||||
if (local) {
|
||||
expectedParams.put("local", String.valueOf(local));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setRandomLocal(MasterNodeReadRequest<?> request, Map<String, String> expectedParams) {
|
||||
setRandomLocal(request::local, expectedParams);
|
||||
}
|
||||
|
||||
private static void setRandomTimeout(Consumer<String> setter, TimeValue defaultTimeout, Map<String, String> expectedParams) {
|
||||
if (randomBoolean()) {
|
||||
String timeout = randomTimeValue();
|
||||
|
|
|
@ -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.GetFieldMappingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse;
|
||||
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;
|
||||
|
@ -703,6 +705,110 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase
|
|||
}
|
||||
}
|
||||
|
||||
public void testGetFieldMapping() throws IOException, InterruptedException {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
|
||||
{
|
||||
CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter"), RequestOptions.DEFAULT);
|
||||
assertTrue(createIndexResponse.isAcknowledged());
|
||||
PutMappingRequest request = new PutMappingRequest("twitter");
|
||||
request.type("tweet");
|
||||
request.source(
|
||||
"{\n" +
|
||||
" \"properties\": {\n" +
|
||||
" \"message\": {\n" +
|
||||
" \"type\": \"text\"\n" +
|
||||
" },\n" +
|
||||
" \"timestamp\": {\n" +
|
||||
" \"type\": \"date\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}", // <1>
|
||||
XContentType.JSON);
|
||||
PutMappingResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT);
|
||||
assertTrue(putMappingResponse.isAcknowledged());
|
||||
}
|
||||
|
||||
// tag::get-field-mapping-request
|
||||
GetFieldMappingsRequest request = new GetFieldMappingsRequest(); // <1>
|
||||
request.indices("twitter"); // <2>
|
||||
request.types("tweet"); // <3>
|
||||
request.fields("message", "timestamp"); // <4>
|
||||
// end::get-field-mapping-request
|
||||
|
||||
// tag::get-field-mapping-request-indicesOptions
|
||||
request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1>
|
||||
// end::get-field-mapping-request-indicesOptions
|
||||
|
||||
// tag::get-field-mapping-request-local
|
||||
request.local(true); // <1>
|
||||
// end::get-field-mapping-request-local
|
||||
|
||||
{
|
||||
|
||||
// tag::get-field-mapping-execute
|
||||
GetFieldMappingsResponse response =
|
||||
client.indices().getFieldMapping(request, RequestOptions.DEFAULT);
|
||||
// end::get-field-mapping-execute
|
||||
|
||||
// tag::get-field-mapping-response
|
||||
final Map<String, Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetaData>>> mappings =
|
||||
response.mappings();// <1>
|
||||
final Map<String, GetFieldMappingsResponse.FieldMappingMetaData> typeMappings =
|
||||
mappings.get("twitter").get("tweet"); // <2>
|
||||
final GetFieldMappingsResponse.FieldMappingMetaData metaData =
|
||||
typeMappings.get("message");// <3>
|
||||
|
||||
final String fullName = metaData.fullName();// <4>
|
||||
final Map<String, Object> source = metaData.sourceAsMap(); // <5>
|
||||
// end::get-field-mapping-response
|
||||
}
|
||||
|
||||
{
|
||||
// tag::get-field-mapping-execute-listener
|
||||
ActionListener<GetFieldMappingsResponse> listener =
|
||||
new ActionListener<GetFieldMappingsResponse>() {
|
||||
@Override
|
||||
public void onResponse(GetFieldMappingsResponse putMappingResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
// end::get-field-mapping-execute-listener
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final ActionListener<GetFieldMappingsResponse> latchListener = new LatchedActionListener<>(listener, latch);
|
||||
listener = ActionListener.wrap(r -> {
|
||||
final Map<String, Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetaData>>> mappings =
|
||||
r.mappings();
|
||||
final Map<String, GetFieldMappingsResponse.FieldMappingMetaData> typeMappings =
|
||||
mappings.get("twitter").get("tweet");
|
||||
final GetFieldMappingsResponse.FieldMappingMetaData metaData1 = typeMappings.get("message");
|
||||
|
||||
final String fullName = metaData1.fullName();
|
||||
final Map<String, Object> source = metaData1.sourceAsMap();
|
||||
latchListener.onResponse(r);
|
||||
}, e -> {
|
||||
latchListener.onFailure(e);
|
||||
fail("should not fail");
|
||||
});
|
||||
|
||||
// tag::get-field-mapping-execute-async
|
||||
client.indices().getFieldMappingAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::get-field-mapping-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void testOpenIndex() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
[[java-rest-high-get-field-mappings]]
|
||||
=== Get Field Mappings API
|
||||
|
||||
[[java-rest-high-get-field-mappings-request]]
|
||||
==== Get Field Mappings Request
|
||||
|
||||
A `GetFieldMappingsRequest` can have an optional list of indices, optional list of types and the list of fields:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-field-mapping-request]
|
||||
--------------------------------------------------
|
||||
<1> An empty request
|
||||
<2> Setting the indices to fetch mapping for
|
||||
<3> The types to be returned
|
||||
<4> The fields 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-field-mapping-request-indicesOptions]
|
||||
--------------------------------------------------
|
||||
<1> Setting `IndicesOptions` controls how unavailable indices are resolved and
|
||||
how wildcard expressions are expanded
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-field-mapping-request-local]
|
||||
--------------------------------------------------
|
||||
<1> The `local` flag (defaults to `false`) controls whether the aliases need
|
||||
to be looked up in the local cluster state or in the cluster state held by
|
||||
the elected master node
|
||||
|
||||
[[java-rest-high-get-field-mappings-sync]]
|
||||
==== Synchronous Execution
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-field-mapping-execute]
|
||||
--------------------------------------------------
|
||||
|
||||
[[java-rest-high-get-field-mapping-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
The asynchronous execution of a get mappings request requires both the
|
||||
`GetFieldMappingsRequest` 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-field-mapping-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `GetFieldMappingsRequest` 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-field-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-field-mapping-response]]
|
||||
==== Get Field Mappings Response
|
||||
|
||||
The returned `GetFieldMappingsResponse` allows to retrieve information about the
|
||||
executed operation as follows:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-field-mapping-response]
|
||||
--------------------------------------------------
|
||||
<1> Returning all requested indices fields' mappings
|
||||
<2> Retrieving the mappings for a particular index and type
|
||||
<3> Getting the mappings metadata for the `message` field
|
||||
<4> Getting the full name of the field
|
||||
<5> Getting the mapping source of the field
|
||||
|
|
@ -77,6 +77,7 @@ Index Management::
|
|||
|
||||
Mapping Management::
|
||||
* <<java-rest-high-put-mapping>>
|
||||
* <<java-rest-high-get-field-mappings>>
|
||||
|
||||
Alias Management::
|
||||
* <<java-rest-high-update-aliases>>
|
||||
|
@ -98,6 +99,7 @@ include::indices/force_merge.asciidoc[]
|
|||
include::indices/rollover.asciidoc[]
|
||||
include::indices/put_mapping.asciidoc[]
|
||||
include::indices/get_mappings.asciidoc[]
|
||||
include::indices/get_field_mappings.asciidoc[]
|
||||
include::indices/update_aliases.asciidoc[]
|
||||
include::indices/exists_alias.asciidoc[]
|
||||
include::indices/get_alias.asciidoc[]
|
||||
|
|
|
@ -20,13 +20,17 @@
|
|||
package org.elasticsearch.action.admin.indices.mapping.get;
|
||||
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
|
||||
|
@ -34,13 +38,45 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.unmodifiableMap;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
|
||||
|
||||
/** Response object for {@link GetFieldMappingsRequest} API */
|
||||
public class GetFieldMappingsResponse extends ActionResponse implements ToXContentFragment {
|
||||
|
||||
private static final ParseField MAPPINGS = new ParseField("mappings");
|
||||
|
||||
private static final ObjectParser<Map<String, Map<String, FieldMappingMetaData>>, String> PARSER =
|
||||
new ObjectParser<>(MAPPINGS.getPreferredName(), true, HashMap::new);
|
||||
|
||||
static {
|
||||
PARSER.declareField((p, typeMappings, index) -> {
|
||||
p.nextToken();
|
||||
while (p.currentToken() == XContentParser.Token.FIELD_NAME) {
|
||||
final String typeName = p.currentName();
|
||||
|
||||
if (p.nextToken() == XContentParser.Token.START_OBJECT) {
|
||||
final Map<String, FieldMappingMetaData> typeMapping = new HashMap<>();
|
||||
typeMappings.put(typeName, typeMapping);
|
||||
|
||||
while (p.nextToken() == XContentParser.Token.FIELD_NAME) {
|
||||
final String fieldName = p.currentName();
|
||||
final FieldMappingMetaData fieldMappingMetaData = FieldMappingMetaData.fromXContent(p);
|
||||
typeMapping.put(fieldName, fieldMappingMetaData);
|
||||
}
|
||||
} else {
|
||||
p.skipChildren();
|
||||
}
|
||||
p.nextToken();
|
||||
}
|
||||
}, MAPPINGS, ObjectParser.ValueType.OBJECT);
|
||||
}
|
||||
|
||||
private Map<String, Map<String, Map<String, FieldMappingMetaData>>> mappings = emptyMap();
|
||||
|
||||
GetFieldMappingsResponse(Map<String, Map<String, Map<String, FieldMappingMetaData>>> mappings) {
|
||||
|
@ -77,7 +113,7 @@ public class GetFieldMappingsResponse extends ActionResponse implements ToXConte
|
|||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
for (Map.Entry<String, Map<String, Map<String, FieldMappingMetaData>>> indexEntry : mappings.entrySet()) {
|
||||
builder.startObject(indexEntry.getKey());
|
||||
builder.startObject("mappings");
|
||||
builder.startObject(MAPPINGS.getPreferredName());
|
||||
for (Map.Entry<String, Map<String, FieldMappingMetaData>> typeEntry : indexEntry.getValue().entrySet()) {
|
||||
builder.startObject(typeEntry.getKey());
|
||||
for (Map.Entry<String, FieldMappingMetaData> fieldEntry : typeEntry.getValue().entrySet()) {
|
||||
|
@ -93,9 +129,46 @@ public class GetFieldMappingsResponse extends ActionResponse implements ToXConte
|
|||
return builder;
|
||||
}
|
||||
|
||||
public static GetFieldMappingsResponse fromXContent(XContentParser parser) throws IOException {
|
||||
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
|
||||
|
||||
final Map<String, Map<String, Map<String, FieldMappingMetaData>>> mappings = new HashMap<>();
|
||||
if (parser.nextToken() == XContentParser.Token.FIELD_NAME) {
|
||||
while (parser.currentToken() == XContentParser.Token.FIELD_NAME) {
|
||||
final String index = parser.currentName();
|
||||
|
||||
final Map<String, Map<String, FieldMappingMetaData>> typeMappings = PARSER.parse(parser, index);
|
||||
mappings.put(index, typeMappings);
|
||||
|
||||
parser.nextToken();
|
||||
}
|
||||
}
|
||||
|
||||
return new GetFieldMappingsResponse(mappings);
|
||||
}
|
||||
|
||||
public static class FieldMappingMetaData implements ToXContentFragment {
|
||||
public static final FieldMappingMetaData NULL = new FieldMappingMetaData("", BytesArray.EMPTY);
|
||||
|
||||
private static final ParseField FULL_NAME = new ParseField("full_name");
|
||||
private static final ParseField MAPPING = new ParseField("mapping");
|
||||
|
||||
private static final ConstructingObjectParser<FieldMappingMetaData, String> PARSER =
|
||||
new ConstructingObjectParser<>("field_mapping_meta_data", true,
|
||||
a -> new FieldMappingMetaData((String)a[0], (BytesReference)a[1])
|
||||
);
|
||||
|
||||
static {
|
||||
PARSER.declareField(optionalConstructorArg(),
|
||||
(p, c) -> p.text(), FULL_NAME, ObjectParser.ValueType.STRING);
|
||||
PARSER.declareField(optionalConstructorArg(),
|
||||
(p, c) -> {
|
||||
final XContentBuilder jsonBuilder = jsonBuilder().copyCurrentStructure(p);
|
||||
final BytesReference bytes = BytesReference.bytes(jsonBuilder);
|
||||
return bytes;
|
||||
}, MAPPING, ObjectParser.ValueType.OBJECT);
|
||||
}
|
||||
|
||||
private String fullName;
|
||||
private BytesReference source;
|
||||
|
||||
|
@ -122,18 +195,41 @@ public class GetFieldMappingsResponse extends ActionResponse implements ToXConte
|
|||
return source;
|
||||
}
|
||||
|
||||
public static FieldMappingMetaData fromXContent(XContentParser parser) throws IOException {
|
||||
return PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.field("full_name", fullName);
|
||||
builder.field(FULL_NAME.getPreferredName(), fullName);
|
||||
if (params.paramAsBoolean("pretty", false)) {
|
||||
builder.field("mapping", sourceAsMap());
|
||||
} else {
|
||||
try (InputStream stream = source.streamInput()) {
|
||||
builder.rawField("mapping", stream, XContentType.JSON);
|
||||
builder.rawField(MAPPING.getPreferredName(), stream, XContentType.JSON);
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FieldMappingMetaData{fullName='" + fullName + '\'' + ", source=" + source + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof FieldMappingMetaData)) return false;
|
||||
FieldMappingMetaData that = (FieldMappingMetaData) o;
|
||||
return Objects.equals(fullName, that.fullName) &&
|
||||
Objects.equals(source, that.source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fullName, source);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -178,4 +274,25 @@ public class GetFieldMappingsResponse extends ActionResponse implements ToXConte
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GetFieldMappingsResponse{" +
|
||||
"mappings=" + mappings +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof GetFieldMappingsResponse)) return false;
|
||||
GetFieldMappingsResponse that = (GetFieldMappingsResponse) o;
|
||||
return Objects.equals(mappings, that.mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mappings);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,16 +23,22 @@ import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsRespon
|
|||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class GetFieldMappingsResponseTests extends ESTestCase {
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
public void testSerialization() throws IOException {
|
||||
public class GetFieldMappingsResponseTests extends AbstractStreamableXContentTestCase<GetFieldMappingsResponse> {
|
||||
|
||||
public void testManualSerialization() throws IOException {
|
||||
Map<String, Map<String, Map<String, FieldMappingMetaData>>> mappings = new HashMap<>();
|
||||
FieldMappingMetaData fieldMappingMetaData = new FieldMappingMetaData("my field", new BytesArray("{}"));
|
||||
mappings.put("index", Collections.singletonMap("type", Collections.singletonMap("field", fieldMappingMetaData)));
|
||||
|
@ -49,4 +55,92 @@ public class GetFieldMappingsResponseTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testManualJunkedJson() throws Exception {
|
||||
// in fact random fields could be evaluated as proper mapping, while proper junk in this case is arrays and values
|
||||
final String json =
|
||||
"{\"index1\":{\"mappings\":"
|
||||
+ "{\"doctype0\":{\"field1\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}},"
|
||||
+ "\"field0\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}}},"
|
||||
// junk here
|
||||
+ "\"junk1\": [\"field1\", {\"field2\":{}}],"
|
||||
+ "\"junk2\": [{\"field3\":{}}],"
|
||||
+ "\"junk3\": 42,"
|
||||
+ "\"junk4\": \"Q\","
|
||||
+ "\"doctype1\":{\"field1\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}},"
|
||||
+ "\"field0\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}}}}},"
|
||||
+ "\"index0\":{\"mappings\":"
|
||||
+ "{\"doctype0\":{\"field1\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}},"
|
||||
+ "\"field0\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}}},"
|
||||
+ "\"doctype1\":{\"field1\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}},"
|
||||
+ "\"field0\":{\"full_name\":\"my field\",\"mapping\":{\"type\":\"keyword\"}}}}}}";
|
||||
|
||||
final XContentParser parser = XContentType.JSON.xContent().createParser(xContentRegistry(),
|
||||
LoggingDeprecationHandler.INSTANCE, json.getBytes("UTF-8"));
|
||||
|
||||
final GetFieldMappingsResponse response = GetFieldMappingsResponse.fromXContent(parser);
|
||||
|
||||
FieldMappingMetaData fieldMappingMetaData =
|
||||
new FieldMappingMetaData("my field", new BytesArray("{\"type\":\"keyword\"}"));
|
||||
Map<String, FieldMappingMetaData> fieldMapping = new HashMap<>();
|
||||
fieldMapping.put("field0", fieldMappingMetaData);
|
||||
fieldMapping.put("field1", fieldMappingMetaData);
|
||||
|
||||
Map<String, Map<String, FieldMappingMetaData>> typeMapping = new HashMap<>();
|
||||
typeMapping.put("doctype0", fieldMapping);
|
||||
typeMapping.put("doctype1", fieldMapping);
|
||||
|
||||
Map<String, Map<String, Map<String, FieldMappingMetaData>>> mappings = new HashMap<>();
|
||||
mappings.put("index0", typeMapping);
|
||||
mappings.put("index1", typeMapping);
|
||||
|
||||
final Map<String, Map<String, Map<String, FieldMappingMetaData>>> responseMappings = response.mappings();
|
||||
assertThat(responseMappings, equalTo(mappings));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetFieldMappingsResponse doParseInstance(XContentParser parser) throws IOException {
|
||||
return GetFieldMappingsResponse.fromXContent(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetFieldMappingsResponse createBlankInstance() {
|
||||
return new GetFieldMappingsResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetFieldMappingsResponse createTestInstance() {
|
||||
return new GetFieldMappingsResponse(randomMapping());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
||||
// allow random fields at the level of `index` and `index.mappings.doctype.field`
|
||||
// otherwise random field could be evaluated as index name or type name
|
||||
return s -> false == (s.matches("(?<index>[^.]+)")
|
||||
|| s.matches("(?<index>[^.]+)\\.mappings\\.(?<doctype>[^.]+)\\.(?<field>[^.]+)"));
|
||||
}
|
||||
|
||||
private Map<String, Map<String, Map<String, FieldMappingMetaData>>> randomMapping() {
|
||||
Map<String, Map<String, Map<String, FieldMappingMetaData>>> mappings = new HashMap<>();
|
||||
|
||||
int indices = randomInt(10);
|
||||
for(int i = 0; i < indices; i++) {
|
||||
final Map<String, Map<String, FieldMappingMetaData>> doctypesMappings = new HashMap<>();
|
||||
int doctypes = randomInt(10);
|
||||
for(int j = 0; j < doctypes; j++) {
|
||||
Map<String, FieldMappingMetaData> fieldMappings = new HashMap<>();
|
||||
int fields = randomInt(10);
|
||||
for(int k = 0; k < fields; k++) {
|
||||
final String mapping = randomBoolean() ? "{\"type\":\"string\"}" : "{\"type\":\"keyword\"}";
|
||||
FieldMappingMetaData metaData =
|
||||
new FieldMappingMetaData("my field", new BytesArray(mapping));
|
||||
fieldMappings.put("field" + k, metaData);
|
||||
}
|
||||
doctypesMappings.put("doctype" + j, fieldMappings);
|
||||
}
|
||||
mappings.put("index" + i, doctypesMappings);
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue