/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate 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.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.metadata.doc;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import com.google.common.base.MoreObjects;
import com.google.common.collect.*;
import io.crate.Constants;
import io.crate.metadata.IndexMappings;
import io.crate.Version;
import io.crate.action.sql.SessionContext;
import io.crate.analyze.NumberOfReplicas;
import io.crate.analyze.ParamTypeHints;
import io.crate.analyze.TableParameterInfo;
import io.crate.analyze.expressions.ExpressionAnalysisContext;
import io.crate.analyze.expressions.ExpressionAnalyzer;
import io.crate.analyze.expressions.TableReferenceResolver;
import io.crate.exceptions.TableAliasSchemaException;
import io.crate.metadata.*;
import io.crate.metadata.table.ColumnPolicy;
import io.crate.metadata.table.Operation;
import io.crate.sql.parser.SqlParser;
import io.crate.sql.tree.Expression;
import io.crate.types.ArrayType;
import io.crate.types.DataType;
import io.crate.types.DataTypes;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.TransportPutIndexTemplateAction;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentHelper;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;
public class DocIndexMetaData {
private static final String ID = "_id";
private final IndexMetaData metaData;
private final Map<String, Object> mappingMap;
private final Map<ColumnIdent, IndexReference.Builder> indicesBuilder = new HashMap<>();
private final ImmutableSortedSet.Builder<Reference> columnsBuilder = ImmutableSortedSet.orderedBy(
Comparator.comparing(o -> o.ident().columnIdent().fqn()));
// columns should be ordered
private final ImmutableMap.Builder<ColumnIdent, Reference> referencesBuilder = ImmutableSortedMap.naturalOrder();
private final ImmutableList.Builder<Reference> partitionedByColumnsBuilder = ImmutableList.builder();
private final ImmutableList.Builder<GeneratedReference> generatedColumnReferencesBuilder = ImmutableList.builder();
private final Functions functions;
private final TableIdent ident;
private final int numberOfShards;
private final BytesRef numberOfReplicas;
private final ImmutableMap<String, Object> tableParameters;
private final Map<String, Object> indicesMap;
private final List<List<String>> partitionedByList;
private final Set<Operation> supportedOperations;
private ImmutableList<Reference> columns;
private ImmutableMap<ColumnIdent, IndexReference> indices;
private ImmutableList<Reference> partitionedByColumns;
private ImmutableList<GeneratedReference> generatedColumnReferences;
private ImmutableMap<ColumnIdent, Reference> references;
private ImmutableList<ColumnIdent> primaryKey;
private ImmutableCollection<ColumnIdent> notNullColumns;
private ColumnIdent routingCol;
private ImmutableList<ColumnIdent> partitionedBy;
private final boolean isAlias;
private final Set<String> aliases;
private boolean hasAutoGeneratedPrimaryKey = false;
private ColumnPolicy columnPolicy = ColumnPolicy.DYNAMIC;
private Map<String, String> generatedColumns;
@Nullable
private final Version versionCreated;
@Nullable
private final Version versionUpgraded;
DocIndexMetaData(Functions functions, IndexMetaData metaData, TableIdent ident) throws IOException {
this.functions = functions;
this.ident = ident;
this.metaData = metaData;
this.isAlias = !metaData.getIndex().getName().equals(ident.indexName());
this.numberOfShards = metaData.getNumberOfShards();
Settings settings = metaData.getSettings();
this.numberOfReplicas = NumberOfReplicas.fromSettings(settings);
this.aliases = ImmutableSet.copyOf(metaData.getAliases().keys().toArray(String.class));
this.mappingMap = getMappingMap(metaData);
this.tableParameters = TableParameterInfo.tableParametersFromIndexMetaData(metaData);
Map<String, Object> metaMap = getNested(mappingMap, "_meta");
indicesMap = getNested(metaMap, "indices", ImmutableMap.<String, Object>of());
partitionedByList = getNested(metaMap, "partitioned_by", ImmutableList.<List<String>>of());
generatedColumns = getNested(metaMap, "generated_columns", ImmutableMap.<String, String>of());
if (isAlias && partitionedByList.isEmpty()) {
supportedOperations = Operation.SYS_READ_ONLY;
} else {
supportedOperations = Operation.buildFromIndexSettingsAndState(metaData.getSettings(), metaData.getState());
}
versionCreated = getVersionCreated(mappingMap);
versionUpgraded = getVersionUpgraded(mappingMap);
}
private static Map<String, Object> getMappingMap(IndexMetaData metaData) throws IOException {
MappingMetaData mappingMetaData = metaData.mappingOrDefault(Constants.DEFAULT_MAPPING_TYPE);
if (mappingMetaData == null) {
return ImmutableMap.of();
}
return mappingMetaData.sourceAsMap();
}
private static Map<String, Object> getVersionMap(Map<String, Object> mappingMap) {
return getNested(getNested(mappingMap, "_meta", null), IndexMappings.VERSION_STRING, null);
}
public static String getRoutingHashFunction(Map<String, Object> mappingMap) {
return getNested(getNested(mappingMap, "_meta", null), IndexMappings.SETTING_ROUTING_HASH_FUNCTION, null);
}
public static String getRoutingHashFunctionPrettyName(String routingHashFunction) {
return IndexMappings.routingHashFunctionPrettyNameLookupMap.getOrDefault(routingHashFunction, routingHashFunction);
}
@Nullable
public static Version getVersionCreated(Map<String, Object> mappingMap) {
Map<String, Object> versionMap = getVersionMap(mappingMap);
return Version.fromMap(getNested(versionMap, Version.Property.CREATED.toString(), null));
}
@Nullable
public static Version getVersionUpgraded(Map<String, Object> mappingMap) {
Map<String, Object> versionMap = getVersionMap(mappingMap);
return Version.fromMap(getNested(versionMap, Version.Property.UPGRADED.toString(), null));
}
@SuppressWarnings("unchecked")
private static <T> T getNested(Map map, String key) {
return (T) map.get(key);
}
private static <T> T getNested(@Nullable Map map, String key, T defaultValue) {
if (map == null) {
return defaultValue;
}
Object o = map.get(key);
if (o == null) {
return defaultValue;
}
//noinspection unchecked
return (T) o;
}
private void addPartitioned(ColumnIdent column, DataType type) {
add(column, type, ColumnPolicy.DYNAMIC, Reference.IndexType.NOT_ANALYZED, true, true);
}
private void add(ColumnIdent column, DataType type, Reference.IndexType indexType, boolean isNotNull) {
add(column, type, ColumnPolicy.DYNAMIC, indexType, false, isNotNull);
}
private void add(ColumnIdent column,
DataType type,
ColumnPolicy columnPolicy,
Reference.IndexType indexType,
boolean partitioned,
boolean isNotNull) {
Reference info;
String generatedExpression = generatedColumns.get(column.fqn());
if (generatedExpression == null) {
info = newInfo(column, type, columnPolicy, indexType, isNotNull);
} else {
info = newGeneratedColumnInfo(column, type, columnPolicy, indexType, generatedExpression, isNotNull);
}
// don't add it if there is a partitioned equivalent of this column
if (partitioned || !(partitionedBy != null && partitionedBy.contains(column))) {
if (info.ident().isColumn()) {
columnsBuilder.add(info);
}
referencesBuilder.put(info.ident().columnIdent(), info);
if (info instanceof GeneratedReference) {
generatedColumnReferencesBuilder.add((GeneratedReference) info);
}
}
if (partitioned) {
partitionedByColumnsBuilder.add(info);
}
}
private void addGeoReference(ColumnIdent column,
@Nullable String tree,
@Nullable String precision,
@Nullable Integer treeLevels,
@Nullable Double distanceErrorPct) {
GeoReference info = new GeoReference(
refIdent(column),
tree,
precision,
treeLevels,
distanceErrorPct);
columnsBuilder.add(info);
referencesBuilder.put(column, info);
}
private ReferenceIdent refIdent(ColumnIdent column) {
return new ReferenceIdent(ident, column);
}
private GeneratedReference newGeneratedColumnInfo(ColumnIdent column,
DataType type,
ColumnPolicy columnPolicy,
Reference.IndexType indexType,
String generatedExpression,
boolean isNotNull) {
return new GeneratedReference(
refIdent(column), granularity(column), type, columnPolicy, indexType, generatedExpression, isNotNull);
}
private RowGranularity granularity(ColumnIdent column) {
if (partitionedBy.contains(column)) {
return RowGranularity.PARTITION;
}
return RowGranularity.DOC;
}
private Reference newInfo(ColumnIdent column,
DataType type,
ColumnPolicy columnPolicy,
Reference.IndexType indexType,
boolean nullable) {
return new Reference(refIdent(column), granularity(column), type, columnPolicy, indexType, nullable);
}
/**
* extract dataType from given columnProperties
*
* @param columnProperties map of String to Object containing column properties
* @return dataType of the column with columnProperties
*/
static DataType getColumnDataType(Map<String, Object> columnProperties) {
DataType type;
String typeName = (String) columnProperties.get("type");
if (typeName == null) {
if (columnProperties.containsKey("properties")) {
type = DataTypes.OBJECT;
} else {
return DataTypes.NOT_SUPPORTED;
}
} else if (typeName.equalsIgnoreCase("array")) {
Map<String, Object> innerProperties = getNested(columnProperties, "inner");
DataType innerType = getColumnDataType(innerProperties);
type = new ArrayType(innerType);
} else {
typeName = typeName.toLowerCase(Locale.ENGLISH);
type = MoreObjects.firstNonNull(DataTypes.ofMappingName(typeName), DataTypes.NOT_SUPPORTED);
}
return type;
}
/**
* Get the IndexType from columnProperties.
* <br />
* Properties might look like:
* <pre>
* {
* "type": "integer"
* }
*
*
* {
* "type": "text",
* "analyzer": "english"
* }
*
*
* {
* "type": "text",
* "fields": {
* "keyword": {
* "type": "keyword",
* "ignore_above": "256"
* }
* }
* }
*
* {
* "type": "date",
* "index": "no"
* }
*
* {
* "type": "keyword",
* "index": false
* }
* </pre>
*/
private static Reference.IndexType getColumnIndexType(Map<String, Object> columnProperties) {
Object index = columnProperties.get("index");
if (index == null) {
if ("text".equals(columnProperties.get("type"))) {
return Reference.IndexType.ANALYZED;
}
return Reference.IndexType.NOT_ANALYZED;
}
if (Boolean.FALSE.equals(index) || "no".equals(index) || "false".equals(index)) {
return Reference.IndexType.NO;
}
if ("not_analyzed".equals(index)) {
return Reference.IndexType.NOT_ANALYZED;
}
return Reference.IndexType.ANALYZED;
}
private static ColumnIdent childIdent(@Nullable ColumnIdent ident, String name) {
if (ident == null) {
return new ColumnIdent(name);
}
return ColumnIdent.getChild(ident, name);
}
/**
* extracts index definitions as well
*/
@SuppressWarnings("unchecked")
private void internalExtractColumnDefinitions(@Nullable ColumnIdent columnIdent,
@Nullable Map<String, Object> propertiesMap) {
if (propertiesMap == null) {
return;
}
for (Map.Entry<String, Object> columnEntry : propertiesMap.entrySet()) {
Map<String, Object> columnProperties = (Map) columnEntry.getValue();
DataType columnDataType = getColumnDataType(columnProperties);
ColumnIdent newIdent = childIdent(columnIdent, columnEntry.getKey());
boolean nullable = !notNullColumns.contains(newIdent);
columnProperties = furtherColumnProperties(columnProperties);
Reference.IndexType columnIndexType = getColumnIndexType(columnProperties);
if (columnDataType == DataTypes.GEO_SHAPE) {
String geoTree = (String) columnProperties.get("tree");
String precision = (String) columnProperties.get("precision");
Integer treeLevels = (Integer) columnProperties.get("tree_levels");
Double distanceErrorPct = (Double) columnProperties.get("distance_error_pct");
addGeoReference(newIdent, geoTree, precision, treeLevels, distanceErrorPct);
} else if (columnDataType == DataTypes.OBJECT
|| (columnDataType.id() == ArrayType.ID
&& ((ArrayType) columnDataType).innerType() == DataTypes.OBJECT)) {
ColumnPolicy columnPolicy =
ColumnPolicy.of(columnProperties.get("dynamic"));
add(newIdent, columnDataType, columnPolicy, Reference.IndexType.NO, false, nullable);
if (columnProperties.get("properties") != null) {
// walk nested
internalExtractColumnDefinitions(newIdent, (Map<String, Object>) columnProperties.get("properties"));
}
} else if (columnDataType != DataTypes.NOT_SUPPORTED) {
List<String> copyToColumns = getNested(columnProperties, "copy_to");
// extract columns this column is copied to, needed for indices
if (copyToColumns != null) {
for (String copyToColumn : copyToColumns) {
ColumnIdent targetIdent = ColumnIdent.fromPath(copyToColumn);
IndexReference.Builder builder = getOrCreateIndexBuilder(targetIdent);
builder.addColumn(newInfo(newIdent, columnDataType, ColumnPolicy.DYNAMIC, columnIndexType, false));
}
}
// is it an index?
if (indicesMap.containsKey(newIdent.fqn())) {
IndexReference.Builder builder = getOrCreateIndexBuilder(newIdent);
builder.indexType(columnIndexType)
.analyzer((String) columnProperties.get("analyzer"));
} else {
add(newIdent, columnDataType, columnIndexType, nullable);
}
}
}
}
/**
* get the real column properties from a possible array mapping,
* keeping most of this stuff inside "inner"
*/
private Map<String, Object> furtherColumnProperties(Map<String, Object> columnProperties) {
if (columnProperties.get("inner") != null) {
return (Map<String, Object>) columnProperties.get("inner");
} else {
return columnProperties;
}
}
private IndexReference.Builder getOrCreateIndexBuilder(ColumnIdent ident) {
return indicesBuilder.computeIfAbsent(ident, k -> new IndexReference.Builder(refIdent(ident)));
}
private ImmutableList<ColumnIdent> getPrimaryKey() {
Map<String, Object> metaMap = getNested(mappingMap, "_meta");
if (metaMap != null) {
ImmutableList.Builder<ColumnIdent> builder = ImmutableList.builder();
Object pKeys = metaMap.get("primary_keys");
if (pKeys != null) {
if (pKeys instanceof String) {
builder.add(ColumnIdent.fromPath((String) pKeys));
return builder.build();
} else if (pKeys instanceof Collection) {
Collection keys = (Collection) pKeys;
if (!keys.isEmpty()) {
for (Object pkey : keys) {
builder.add(ColumnIdent.fromPath(pkey.toString()));
}
return builder.build();
}
}
}
}
if (getCustomRoutingCol() == null && partitionedByList.isEmpty()) {
hasAutoGeneratedPrimaryKey = true;
return ImmutableList.of(DocSysColumns.ID);
}
return ImmutableList.of();
}
private ImmutableCollection<ColumnIdent> getNotNullColumns() {
Map<String, Object> metaMap = getNested(mappingMap, "_meta");
if (metaMap != null) {
ImmutableSet.Builder<ColumnIdent> builder = ImmutableSet.builder();
Map<String, Object> constraintsMap = getNested(metaMap, "constraints");
if (constraintsMap != null) {
Object notNullColumnsMeta = constraintsMap.get("not_null");
if (notNullColumnsMeta != null) {
Collection notNullColumns = (Collection) notNullColumnsMeta;
if (!notNullColumns.isEmpty()) {
for (Object notNullColumn : notNullColumns) {
builder.add(ColumnIdent.fromPath(notNullColumn.toString()));
}
return builder.build();
}
}
}
}
return ImmutableList.of();
}
private ImmutableList<ColumnIdent> getPartitionedBy() {
ImmutableList.Builder<ColumnIdent> builder = ImmutableList.builder();
for (List<String> partitionedByInfo : partitionedByList) {
builder.add(ColumnIdent.fromPath(partitionedByInfo.get(0)));
}
return builder.build();
}
private ColumnPolicy getColumnPolicy() {
return ColumnPolicy.of(mappingMap.get("dynamic"));
}
private void createColumnDefinitions() {
Map<String, Object> propertiesMap = getNested(mappingMap, "properties");
internalExtractColumnDefinitions(null, propertiesMap);
extractPartitionedByColumns();
}
private ImmutableMap<ColumnIdent, IndexReference> createIndexDefinitions() {
ImmutableMap.Builder<ColumnIdent, IndexReference> builder = ImmutableMap.builder();
for (Map.Entry<ColumnIdent, IndexReference.Builder> entry : indicesBuilder.entrySet()) {
builder.put(entry.getKey(), entry.getValue().build());
}
indices = builder.build();
return indices;
}
private void extractPartitionedByColumns() {
for (Tuple<ColumnIdent, DataType> partitioned : PartitionedByMappingExtractor.extractPartitionedByColumns(partitionedByList)) {
addPartitioned(partitioned.v1(), partitioned.v2());
}
}
private ColumnIdent getCustomRoutingCol() {
if (mappingMap != null) {
Map<String, Object> metaMap = getNested(mappingMap, "_meta");
if (metaMap != null) {
String routingPath = (String) metaMap.get("routing");
if (routingPath != null && !routingPath.equals(ID)) {
return ColumnIdent.fromPath(routingPath);
}
}
}
return null;
}
private ColumnIdent getRoutingCol() {
ColumnIdent col = getCustomRoutingCol();
if (col != null) {
return col;
}
if (primaryKey.size() == 1) {
return primaryKey.get(0);
}
return DocSysColumns.ID;
}
private void initializeGeneratedExpressions() {
if (generatedColumnReferences.isEmpty()) {
return;
}
Collection<Reference> references = this.references.values();
TableReferenceResolver tableReferenceResolver = new TableReferenceResolver(references);
ExpressionAnalyzer expressionAnalyzer = new ExpressionAnalyzer(
functions, SessionContext.SYSTEM_SESSION, ParamTypeHints.EMPTY, tableReferenceResolver, null);
ExpressionAnalysisContext context = new ExpressionAnalysisContext();
for (Reference reference : generatedColumnReferences) {
GeneratedReference generatedReference = (GeneratedReference) reference;
Expression expression = SqlParser.createExpression(generatedReference.formattedGeneratedExpression());
generatedReference.generatedExpression(expressionAnalyzer.convert(expression, context));
generatedReference.referencedReferences(ImmutableList.copyOf(tableReferenceResolver.references()));
tableReferenceResolver.references().clear();
}
}
public DocIndexMetaData build() {
notNullColumns = getNotNullColumns();
partitionedBy = getPartitionedBy();
columnPolicy = getColumnPolicy();
createColumnDefinitions();
indices = createIndexDefinitions();
columns = ImmutableList.copyOf(columnsBuilder.build());
partitionedByColumns = partitionedByColumnsBuilder.build();
DocSysColumns.forTable(ident, referencesBuilder::put);
references = referencesBuilder.build();
generatedColumnReferences = generatedColumnReferencesBuilder.build();
primaryKey = getPrimaryKey();
routingCol = getRoutingCol();
initializeGeneratedExpressions();
return this;
}
public ImmutableMap<ColumnIdent, Reference> references() {
return references;
}
public ImmutableList<Reference> columns() {
return columns;
}
public ImmutableMap<ColumnIdent, IndexReference> indices() {
return indices;
}
public ImmutableList<Reference> partitionedByColumns() {
return partitionedByColumns;
}
ImmutableList<GeneratedReference> generatedColumnReferences() {
return generatedColumnReferences;
}
public ImmutableList<ColumnIdent> primaryKey() {
return primaryKey;
}
ColumnIdent routingCol() {
return routingCol;
}
String getRoutingHashFunction() {
return getRoutingHashFunction(mappingMap);
}
/**
* Returns true if the schema of this and <code>other</code> is the same,
* this includes the table name, as this is reflected in the ReferenceIdents of
* the columns.
*/
boolean schemaEquals(DocIndexMetaData other) {
if (this == other) return true;
if (other == null) return false;
// TODO: when analyzers are exposed in the info, equality has to be checked on them
// see: TransportSQLActionTest.testSelectTableAliasSchemaExceptionColumnDefinition
if (columns != null ? !columns.equals(other.columns) : other.columns != null) return false;
if (primaryKey != null ? !primaryKey.equals(other.primaryKey) : other.primaryKey != null) return false;
if (indices != null ? !indices.equals(other.indices) : other.indices != null) return false;
if (references != null ? !references.equals(other.references) : other.references != null) return false;
if (routingCol != null ? !routingCol.equals(other.routingCol) : other.routingCol != null) return false;
return true;
}
protected DocIndexMetaData merge(DocIndexMetaData other,
TransportPutIndexTemplateAction transportPutIndexTemplateAction,
boolean thisIsCreatedFromTemplate) throws IOException {
if (schemaEquals(other)) {
return this;
} else if (thisIsCreatedFromTemplate) {
boolean mappingChanged = XContentHelper.update(this.mappingMap, other.mappingMap, true);
if (mappingChanged) {
IndexMetaData indexMetaData = mergeIndexMetaData(other.metaData);
DocIndexMetaData ret = new DocIndexMetaData(functions, indexMetaData, other.ident).build();
updateTemplate(ret, transportPutIndexTemplateAction, this.metaData.getSettings());
return ret;
}
return this;
} else {
throw new TableAliasSchemaException(other.ident.name());
}
}
private void updateTemplate(DocIndexMetaData md,
TransportPutIndexTemplateAction transportPutIndexTemplateAction,
Settings updateSettings) {
String templateName = PartitionName.templateName(ident.schema(), ident.name());
PutIndexTemplateRequest request = new PutIndexTemplateRequest(templateName)
.mapping(Constants.DEFAULT_MAPPING_TYPE, md.mappingMap)
.create(false)
.settings(updateSettings.filter(s ->
s.equals(IndexMetaData.SETTING_VERSION_CREATED) == false))
.template(templateName + "*");
for (String alias : md.aliases()) {
request = request.alias(new Alias(alias));
}
transportPutIndexTemplateAction.execute(request);
}
/**
* @return the name of the underlying index even if this table is referenced by alias
*/
String concreteIndexName() {
return metaData.getIndex().getName();
}
public boolean isAlias() {
return isAlias;
}
public Set<String> aliases() {
return aliases;
}
boolean hasAutoGeneratedPrimaryKey() {
return hasAutoGeneratedPrimaryKey;
}
public int numberOfShards() {
return numberOfShards;
}
public BytesRef numberOfReplicas() {
return numberOfReplicas;
}
public ImmutableList<ColumnIdent> partitionedBy() {
return partitionedBy;
}
public ColumnPolicy columnPolicy() {
return columnPolicy;
}
public ImmutableMap<String, Object> tableParameters() {
return tableParameters;
}
private ImmutableMap<ColumnIdent, String> getAnalyzers(ColumnIdent columnIdent, Map<String, Object> propertiesMap) {
ImmutableMap.Builder<ColumnIdent, String> builder = ImmutableMap.builder();
for (Map.Entry<String, Object> columnEntry : propertiesMap.entrySet()) {
Map<String, Object> columnProperties = (Map) columnEntry.getValue();
DataType columnDataType = getColumnDataType(columnProperties);
ColumnIdent newIdent = childIdent(columnIdent, columnEntry.getKey());
columnProperties = furtherColumnProperties(columnProperties);
if (columnDataType == DataTypes.OBJECT
|| (columnDataType.id() == ArrayType.ID
&& ((ArrayType) columnDataType).innerType() == DataTypes.OBJECT)) {
if (columnProperties.get("properties") != null) {
builder.putAll(getAnalyzers(newIdent, (Map<String, Object>) columnProperties.get("properties")));
}
}
String analyzer = (String) columnProperties.get("analyzer");
if (analyzer != null) {
builder.put(newIdent, analyzer);
}
}
return builder.build();
}
ImmutableMap<ColumnIdent, String> analyzers() {
Map<String, Object> propertiesMap = getNested(mappingMap, "properties");
if (propertiesMap == null) {
return ImmutableMap.of();
} else {
return getAnalyzers(null, propertiesMap);
}
}
Set<Operation> supportedOperations() {
return supportedOperations;
}
@Nullable
public Version versionCreated() {
return versionCreated;
}
@Nullable
public Version versionUpgraded() {
return versionUpgraded;
}
/**
* Merges this {@link IndexMetaData} with the given one,
* but comparing to {@link IndexMetaData#builder(IndexMetaData)}, it won't set {@link IndexMetaData#primaryTerms}
* as the number of shards can differ (its applied via the settings).
*/
private IndexMetaData mergeIndexMetaData(IndexMetaData other) {
IndexMetaData.Builder builder = IndexMetaData.builder(other.getIndex().getName())
.settings(this.metaData.getSettings())
.state(other.getState())
.version(other.getVersion());
// mappings are expected to be merged already, so we use this one's
try {
builder.putMapping(new MappingMetaData(Constants.DEFAULT_MAPPING_TYPE, mappingMap));
} catch (IOException e) {
throw new RuntimeException(e);
}
for (ObjectObjectCursor<String, AliasMetaData> cursor : other.getAliases()) {
builder.putAlias(cursor.value);
}
for (ObjectObjectCursor<String, IndexMetaData.Custom> cursor : other.getCustoms()) {
builder.putCustom(cursor.key, cursor.value);
}
return builder.build();
}
}