All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.rest.action.admin.indices.RestGetMappingAction Maven / Gradle / Ivy

/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.rest.action.admin.indices;

import com.carrotsearch.hppc.cursors.ObjectCursor;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ElasticsearchTimeoutException;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.logging.DeprecationCategory;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.StatusToXContentObject;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.http.HttpChannel;
import org.elasticsearch.indices.TypeMissingException;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.DispatchingRestToXContentListener;
import org.elasticsearch.rest.action.RestCancellableNodeClient;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.SortedSet;
import java.util.function.LongSupplier;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.HEAD;

public class RestGetMappingAction extends BaseRestHandler {
    private static final Logger logger = LogManager.getLogger(RestGetMappingAction.class);
    private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(logger.getName());
    public static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Using include_type_name in get"
        + " mapping requests is deprecated. The parameter will be removed in the next major version.";

    private final ThreadPool threadPool;

    public RestGetMappingAction(ThreadPool threadPool) {
        this.threadPool = threadPool;
    }

    @Override
    public List routes() {
        return unmodifiableList(
            asList(
                new Route(GET, "/_mapping"),
                new Route(GET, "/_mappings"),
                new Route(GET, "/{index}/{type}/_mapping"),
                new Route(GET, "/{index}/_mapping"),
                new Route(GET, "/{index}/_mappings"),
                new Route(GET, "/{index}/_mappings/{type}"),
                new Route(GET, "/{index}/_mapping/{type}"),
                new Route(HEAD, "/{index}/_mapping/{type}"),
                new Route(GET, "/_mapping/{type}")
            )
        );
    }

    @Override
    public String getName() {
        return "get_mapping_action";
    }

    @Override
    public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
        final String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
        final String[] types = request.paramAsStringArrayOrEmptyIfAll("type");
        boolean includeTypeName = request.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER, DEFAULT_INCLUDE_TYPE_NAME_POLICY);

        if (request.method().equals(HEAD)) {
            deprecationLogger.critical(
                DeprecationCategory.TYPES,
                "get_mapping_types_removal",
                "Type exists requests are deprecated, as types have been deprecated."
            );
        } else if (includeTypeName == false && types.length > 0) {
            throw new IllegalArgumentException(
                "Types cannot be provided in get mapping requests, unless" + " include_type_name is set to true."
            );
        }
        if (request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)) {
            deprecationLogger.critical(DeprecationCategory.TYPES, "get_mapping_with_types", TYPES_DEPRECATION_MESSAGE);
        }

        final GetMappingsRequest getMappingsRequest = new GetMappingsRequest();
        getMappingsRequest.indices(indices).types(types);
        getMappingsRequest.indicesOptions(IndicesOptions.fromRequest(request, getMappingsRequest.indicesOptions()));
        final TimeValue timeout = request.paramAsTime("master_timeout", getMappingsRequest.masterNodeTimeout());
        getMappingsRequest.masterNodeTimeout(timeout);
        getMappingsRequest.local(request.paramAsBoolean("local", getMappingsRequest.local()));
        final HttpChannel httpChannel = request.getHttpChannel();
        return channel -> new RestCancellableNodeClient(client, httpChannel).admin()
            .indices()
            .getMappings(
                getMappingsRequest,
                new DispatchingRestToXContentListener<>(threadPool.executor(ThreadPool.Names.MANAGEMENT), channel, request).map(
                    getMappingsResponse -> new RestGetMappingsResponse(
                        types,
                        getMappingsResponse,
                        threadPool::relativeTimeInMillis,
                        timeout
                    )
                )
            );
    }

    private static final class RestGetMappingsResponse implements StatusToXContentObject {
        private final String[] types;
        private final GetMappingsResponse response;
        private final LongSupplier relativeTimeSupplierMillis;
        private final TimeValue timeout;
        private final long startTimeMs;
        private final SortedSet missingTypes;

        RestGetMappingsResponse(String[] types, GetMappingsResponse response, LongSupplier relativeTimeSupplierMillis, TimeValue timeout) {
            this.types = types;
            this.response = response;
            this.relativeTimeSupplierMillis = relativeTimeSupplierMillis;
            this.timeout = timeout;
            this.startTimeMs = relativeTimeSupplierMillis.getAsLong();
            this.missingTypes = Collections.unmodifiableSortedSet(getMissingTypes(types, response.getMappings()));
        }

        @Override
        public RestStatus status() {
            return missingTypes.isEmpty() ? RestStatus.OK : RestStatus.NOT_FOUND;
        }

        @Override
        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
            if (relativeTimeSupplierMillis.getAsLong() - startTimeMs > timeout.millis()) {
                throw new ElasticsearchTimeoutException("Timed out getting mappings");
            }

            final ImmutableOpenMap> mappingsByIndex = response.getMappings();
            if (mappingsByIndex.isEmpty() && types.length != 0) {
                builder.close();
                throw new TypeMissingException("_all", String.join(",", types));
            }

            builder.startObject();
            {
                if (missingTypes.isEmpty() == false) {
                    final String message = String.format(
                        Locale.ROOT,
                        "type" + (missingTypes.size() == 1 ? "" : "s") + " [%s] missing",
                        Strings.collectionToCommaDelimitedString(missingTypes)
                    );
                    builder.field("error", message);
                    builder.field("status", status().getStatus());
                }
                response.toXContent(builder, params);
            }
            builder.endObject();

            return builder;
        }

        private static SortedSet getMissingTypes(
            String[] types,
            ImmutableOpenMap> mappingsByIndex
        ) {
            if (mappingsByIndex.isEmpty() && types.length != 0) {
                return Collections.emptySortedSet();
            }
            final Set typeNames = new HashSet<>();
            for (final ImmutableOpenMap value : mappingsByIndex.values()) {
                for (final ObjectCursor inner : value.keys()) {
                    typeNames.add(inner.value);
                }
            }

            final SortedSet difference = Sets.sortedDifference(Arrays.stream(types).collect(Collectors.toSet()), typeNames);

            // now remove requested aliases that contain wildcards that are simple matches
            final List matches = new ArrayList<>();
            outer: for (final String pattern : difference) {
                if (pattern.contains("*")) {
                    for (final String typeName : typeNames) {
                        if (Regex.simpleMatch(pattern, typeName)) {
                            matches.add(pattern);
                            continue outer;
                        }
                    }
                }
            }
            difference.removeAll(matches);
            return difference;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy