org.elasticsearch.cluster.metadata.IndexTemplateMetaData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* 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.cluster.metadata;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.AbstractDiffable;
import org.elasticsearch.cluster.Diff;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.DeprecationLogger;
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.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class IndexTemplateMetaData extends AbstractDiffable {
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(LogManager.getLogger(IndexTemplateMetaData.class));
private final String name;
private final int order;
/**
* The version is an arbitrary number managed by the user so that they can easily and quickly verify the existence of a given template.
* Expected usage:
*
* PUT /_template/my_template
* {
* "index_patterns": ["my_index-*"],
* "mappings": { ... },
* "version": 1
* }
*
* Then, some process from the user can occasionally verify that the template exists with the appropriate version without having to
* check the template's content:
*
* GET /_template/my_template?filter_path=*.version
*
*/
@Nullable
private final Integer version;
private final List patterns;
private final Settings settings;
// the mapping source should always include the type as top level
private final ImmutableOpenMap mappings;
private final ImmutableOpenMap aliases;
public IndexTemplateMetaData(String name, int order, Integer version,
List patterns, Settings settings,
ImmutableOpenMap mappings,
ImmutableOpenMap aliases) {
if (patterns == null || patterns.isEmpty()) {
throw new IllegalArgumentException("Index patterns must not be null or empty; got " + patterns);
}
this.name = name;
this.order = order;
this.version = version;
this.patterns= patterns;
this.settings = settings;
this.mappings = mappings;
this.aliases = aliases;
}
public String name() {
return this.name;
}
public int order() {
return this.order;
}
public int getOrder() {
return order();
}
@Nullable
public Integer getVersion() {
return version();
}
@Nullable
public Integer version() {
return version;
}
public String getName() {
return this.name;
}
public List patterns() {
return this.patterns;
}
public List getPatterns() {
return this.patterns;
}
public Settings settings() {
return this.settings;
}
public Settings getSettings() {
return settings();
}
public ImmutableOpenMap mappings() {
return this.mappings;
}
public ImmutableOpenMap getMappings() {
return this.mappings;
}
public ImmutableOpenMap aliases() {
return this.aliases;
}
public ImmutableOpenMap getAliases() {
return this.aliases;
}
public static Builder builder(String name) {
return new Builder(name);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IndexTemplateMetaData that = (IndexTemplateMetaData) o;
if (order != that.order) return false;
if (!mappings.equals(that.mappings)) return false;
if (!name.equals(that.name)) return false;
if (!settings.equals(that.settings)) return false;
if (!patterns.equals(that.patterns)) return false;
return Objects.equals(version, that.version);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + order;
result = 31 * result + Objects.hashCode(version);
result = 31 * result + patterns.hashCode();
result = 31 * result + settings.hashCode();
result = 31 * result + mappings.hashCode();
return result;
}
public static IndexTemplateMetaData readFrom(StreamInput in) throws IOException {
Builder builder = new Builder(in.readString());
builder.order(in.readInt());
if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1)) {
builder.patterns(in.readList(StreamInput::readString));
} else {
builder.patterns(Collections.singletonList(in.readString()));
}
builder.settings(Settings.readSettingsFromStream(in));
int mappingsSize = in.readVInt();
for (int i = 0; i < mappingsSize; i++) {
builder.putMapping(in.readString(), CompressedXContent.readCompressedString(in));
}
int aliasesSize = in.readVInt();
for (int i = 0; i < aliasesSize; i++) {
AliasMetaData aliasMd = new AliasMetaData(in);
builder.putAlias(aliasMd);
}
if (in.getVersion().before(Version.V_6_5_0)) {
// Previously we allowed custom metadata
int customSize = in.readVInt();
assert customSize == 0 : "expected no custom metadata";
if (customSize > 0) {
throw new IllegalStateException("unexpected custom metadata when none is supported");
}
}
if (in.getVersion().onOrAfter(Version.V_5_0_0_beta1)) {
builder.version(in.readOptionalVInt());
}
return builder.build();
}
public static Diff readDiffFrom(StreamInput in) throws IOException {
return readDiffFrom(IndexTemplateMetaData::readFrom, in);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeInt(order);
if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1)) {
out.writeStringList(patterns);
} else {
out.writeString(patterns.get(0));
}
Settings.writeSettingsToStream(settings, out);
out.writeVInt(mappings.size());
for (ObjectObjectCursor cursor : mappings) {
out.writeString(cursor.key);
cursor.value.writeTo(out);
}
out.writeVInt(aliases.size());
for (ObjectCursor cursor : aliases.values()) {
cursor.value.writeTo(out);
}
if (out.getVersion().before(Version.V_6_5_0)) {
out.writeVInt(0);
}
if (out.getVersion().onOrAfter(Version.V_5_0_0_beta1)) {
out.writeOptionalVInt(version);
}
}
public static class Builder {
private static final Set VALID_FIELDS = Sets.newHashSet(
"template", "order", "mappings", "settings", "index_patterns", "aliases", "version");
private String name;
private int order;
private Integer version;
private List indexPatterns;
private Settings settings = Settings.Builder.EMPTY_SETTINGS;
private final ImmutableOpenMap.Builder mappings;
private final ImmutableOpenMap.Builder aliases;
public Builder(String name) {
this.name = name;
mappings = ImmutableOpenMap.builder();
aliases = ImmutableOpenMap.builder();
}
public Builder(IndexTemplateMetaData indexTemplateMetaData) {
this.name = indexTemplateMetaData.name();
order(indexTemplateMetaData.order());
version(indexTemplateMetaData.version());
patterns(indexTemplateMetaData.patterns());
settings(indexTemplateMetaData.settings());
mappings = ImmutableOpenMap.builder(indexTemplateMetaData.mappings());
aliases = ImmutableOpenMap.builder(indexTemplateMetaData.aliases());
}
public Builder order(int order) {
this.order = order;
return this;
}
public Builder version(Integer version) {
this.version = version;
return this;
}
public Builder patterns(List indexPatterns) {
this.indexPatterns = indexPatterns;
return this;
}
public Builder settings(Settings.Builder settings) {
this.settings = settings.build();
return this;
}
public Builder settings(Settings settings) {
this.settings = settings;
return this;
}
public Builder removeMapping(String mappingType) {
mappings.remove(mappingType);
return this;
}
public Builder putMapping(String mappingType, CompressedXContent mappingSource) throws IOException {
mappings.put(mappingType, mappingSource);
return this;
}
public Builder putMapping(String mappingType, String mappingSource) throws IOException {
mappings.put(mappingType, new CompressedXContent(mappingSource));
return this;
}
public Builder putAlias(AliasMetaData aliasMetaData) {
aliases.put(aliasMetaData.alias(), aliasMetaData);
return this;
}
public Builder putAlias(AliasMetaData.Builder aliasMetaData) {
aliases.put(aliasMetaData.alias(), aliasMetaData.build());
return this;
}
public IndexTemplateMetaData build() {
return new IndexTemplateMetaData(name, order, version, indexPatterns, settings, mappings.build(), aliases.build());
}
@SuppressWarnings("unchecked")
public static void toXContent(IndexTemplateMetaData indexTemplateMetaData, XContentBuilder builder, ToXContent.Params params)
throws IOException {
builder.startObject(indexTemplateMetaData.name());
toInnerXContent(indexTemplateMetaData, builder, params);
builder.endObject();
}
public static void toInnerXContent(IndexTemplateMetaData indexTemplateMetaData, XContentBuilder builder, ToXContent.Params params)
throws IOException {
builder.field("order", indexTemplateMetaData.order());
if (indexTemplateMetaData.version() != null) {
builder.field("version", indexTemplateMetaData.version());
}
builder.field("index_patterns", indexTemplateMetaData.patterns());
builder.startObject("settings");
indexTemplateMetaData.settings().toXContent(builder, params);
builder.endObject();
if (params.paramAsBoolean("reduce_mappings", false)) {
builder.startObject("mappings");
for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) {
byte[] mappingSource = cursor.value.uncompressed();
Map mapping = XContentHelper.convertToMap(new BytesArray(mappingSource), true).v2();
if (mapping.size() == 1 && mapping.containsKey(cursor.key)) {
// the type name is the root value, reduce it
mapping = (Map) mapping.get(cursor.key);
}
builder.field(cursor.key);
builder.map(mapping);
}
builder.endObject();
} else {
builder.startArray("mappings");
for (ObjectObjectCursor cursor : indexTemplateMetaData.mappings()) {
byte[] data = cursor.value.uncompressed();
builder.map(XContentHelper.convertToMap(new BytesArray(data), true).v2());
}
builder.endArray();
}
builder.startObject("aliases");
for (ObjectCursor cursor : indexTemplateMetaData.aliases().values()) {
AliasMetaData.Builder.toXContent(cursor.value, builder, params);
}
builder.endObject();
}
public static IndexTemplateMetaData fromXContent(XContentParser parser, String templateName) throws IOException {
Builder builder = new Builder(templateName);
String currentFieldName = skipTemplateName(parser);
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
if ("settings".equals(currentFieldName)) {
Settings.Builder templateSettingsBuilder = Settings.builder();
templateSettingsBuilder.put(Settings.fromXContent(parser));
templateSettingsBuilder.normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX);
builder.settings(templateSettingsBuilder.build());
} else if ("mappings".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
String mappingType = currentFieldName;
Map mappingSource =
MapBuilder.newMapBuilder().put(mappingType, parser.mapOrdered()).map();
builder.putMapping(mappingType, Strings.toString(XContentFactory.jsonBuilder().map(mappingSource)));
}
}
} else if ("aliases".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
builder.putAlias(AliasMetaData.Builder.fromXContent(parser));
}
} else {
throw new ElasticsearchParseException("unknown key [{}] for index template", currentFieldName);
}
} else if (token == XContentParser.Token.START_ARRAY) {
if ("mappings".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
Map mapping = parser.mapOrdered();
if (mapping.size() == 1) {
String mappingType = mapping.keySet().iterator().next();
String mappingSource = Strings.toString(XContentFactory.jsonBuilder().map(mapping));
if (mappingSource == null) {
// crap, no mapping source, warn?
} else {
builder.putMapping(mappingType, mappingSource);
}
}
}
} else if ("index_patterns".equals(currentFieldName)) {
List index_patterns = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
index_patterns.add(parser.text());
}
builder.patterns(index_patterns);
}
} else if (token.isValue()) {
// Prior to 5.1.0, elasticsearch only supported a single index pattern called `template` (#21009)
if("template".equals(currentFieldName)) {
DEPRECATION_LOGGER.deprecated("Deprecated field [template] used, replaced by [index_patterns]");
builder.patterns(Collections.singletonList(parser.text()));
} else if ("order".equals(currentFieldName)) {
builder.order(parser.intValue());
} else if ("version".equals(currentFieldName)) {
builder.version(parser.intValue());
}
}
}
return builder.build();
}
private static String skipTemplateName(XContentParser parser) throws IOException {
XContentParser.Token token = parser.nextToken();
if (token != null && token == XContentParser.Token.START_OBJECT) {
token = parser.nextToken();
if (token == XContentParser.Token.FIELD_NAME) {
String currentFieldName = parser.currentName();
if (VALID_FIELDS.contains(currentFieldName)) {
return currentFieldName;
} else {
// we just hit the template name, which should be ignored and we move on
parser.nextToken();
}
}
}
return null;
}
}
}