From 3afdf4a872e257ec95a4436049907ee825d249e0 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Tue, 28 Jan 2014 10:47:40 +0100 Subject: [PATCH] Added support for aliases to create index api It is now possible to specify aliases during index creation: curl -XPUT 'http://localhost:9200/test' -d ' { "aliases" : { "alias1" : {}, "alias2" : { "filter" : { "term" : {"field":"value"}} } } }' Closes #4920 --- docs/reference/indices/aliases.asciidoc | 24 +- docs/reference/indices/create-index.asciidoc | 23 ++ .../test/indices.create/10_basic.yaml | 41 ++- .../test/indices.get_alias/10_basic.yaml | 28 +-- .../action/admin/indices/alias/Alias.java | 234 ++++++++++++++++++ .../CreateIndexClusterStateUpdateRequest.java | 18 +- .../indices/create/CreateIndexRequest.java | 83 ++++++- .../create/CreateIndexRequestBuilder.java | 33 +++ .../create/TransportCreateIndexAction.java | 2 +- .../cluster/metadata/AliasValidator.java | 94 +++++++ .../metadata/MetaDataCreateIndexService.java | 24 +- .../metadata/MetaDataIndexAliasesService.java | 33 +-- .../aliases/IndexAliasesTests.java | 69 ++++++ 13 files changed, 647 insertions(+), 59 deletions(-) create mode 100644 src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java create mode 100644 src/main/java/org/elasticsearch/cluster/metadata/AliasValidator.java diff --git a/docs/reference/indices/aliases.asciidoc b/docs/reference/indices/aliases.asciidoc index 53f484cadcf..967aa6a5404 100644 --- a/docs/reference/indices/aliases.asciidoc +++ b/docs/reference/indices/aliases.asciidoc @@ -185,7 +185,7 @@ Adding user alias: [source,js] -------------------------------------------------- curl -XPUT 'localhost:9200/users/_alias/user_12' -d '{ - "routing" : "12", + "routing" : "12", "filter" : { "term" : { "user_id" : 12 @@ -194,6 +194,28 @@ curl -XPUT 'localhost:9200/users/_alias/user_12' -d '{ }' -------------------------------------------------- +[float] +[[alias-index-creation]] +=== Aliases during index creation + +coming[1.1.0] + +Aliases can also be specified during <>: + +[source,js] +-------------------------------------------------- +curl -XPUT localhost:9200/logs_20142801 -d '{ + "aliases" : { + "current_day" : {}, + "2014" : { + "filter" : { + "term" : {"year" : 2014 } + } + } + } +}' +-------------------------------------------------- + [float] [[deleting]] === Delete aliases diff --git a/docs/reference/indices/create-index.asciidoc b/docs/reference/indices/create-index.asciidoc index 8d628f5c7cd..c646311d2f3 100644 --- a/docs/reference/indices/create-index.asciidoc +++ b/docs/reference/indices/create-index.asciidoc @@ -103,3 +103,26 @@ curl -XPUT localhost:9200/test -d '{ } }' -------------------------------------------------- + +[float] +[[create-index-aliases]] +=== Aliases + +coming[1.1.0] + +The create index API allows also to provide a set of <>: + +[source,js] +-------------------------------------------------- +curl -XPUT localhost:9200/test -d '{ + "aliases" : { + "alias_1" : {}, + "alias_2" : { + "filter" : { + "term" : {"user" : "kimchy" } + }, + "routing" : "kimchy" + } + } +}' +-------------------------------------------------- diff --git a/rest-api-spec/test/indices.create/10_basic.yaml b/rest-api-spec/test/indices.create/10_basic.yaml index 2cb979b8afb..8e268201658 100644 --- a/rest-api-spec/test/indices.create/10_basic.yaml +++ b/rest-api-spec/test/indices.create/10_basic.yaml @@ -50,7 +50,35 @@ - match: {test_index.warmers.test_warmer.source.query.match_all: {}} --- -"Create index with mappings, settings and warmers": +"Create index with aliases": + + - do: + indices.create: + index: test_index + body: + aliases: + test_alias: {} + test_blias: + routing: b + test_clias: + filter: + term: + field : value + + - do: + indices.get_alias: + index: test_index + + - match: {test_index.aliases.test_alias: {}} + - match: {test_index.aliases.test_blias.search_routing: b} + - match: {test_index.aliases.test_blias.index_routing: b} + - is_false: test_index.aliases.test_blias.filter + - match: {test_index.aliases.test_clias.filter.term.field: value} + - is_false: test_index.aliases.test_clias.index_routing + - is_false: test_index.aliases.test_clias.search_routing + +--- +"Create index with mappings, settings, warmers and aliases": - do: indices.create: @@ -65,6 +93,9 @@ source: query: match_all: {} + aliases: + test_alias: {} + test_blias: {routing: b} - do: indices.get_mapping: @@ -83,3 +114,11 @@ index: test_index - match: { test_index.warmers.test_warmer.source.query.match_all: {}} + + - do: + indices.get_alias: + index: test_index + + - match: { test_index.aliases.test_alias: {}} + - match: { test_index.aliases.test_blias.search_routing: b} + - match: { test_index.aliases.test_blias.index_routing: b} diff --git a/rest-api-spec/test/indices.get_alias/10_basic.yaml b/rest-api-spec/test/indices.get_alias/10_basic.yaml index 42b696f3125..22a67a99144 100644 --- a/rest-api-spec/test/indices.get_alias/10_basic.yaml +++ b/rest-api-spec/test/indices.get_alias/10_basic.yaml @@ -4,30 +4,18 @@ setup: - do: indices.create: index: test_index + body: + aliases: + test_alias: {} + test_blias: {} - do: indices.create: index: test_index_2 - - - do: - indices.put_alias: - index: test_index - name: test_alias - - - do: - indices.put_alias: - index: test_index - name: test_blias - - - do: - indices.put_alias: - index: test_index_2 - name: test_alias - - - do: - indices.put_alias: - index: test_index_2 - name: test_blias + body: + aliases: + test_alias: {} + test_blias: {} --- "Get all aliases via /_alias": diff --git a/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java b/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java new file mode 100644 index 00000000000..36471c856d4 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java @@ -0,0 +1,234 @@ +/* + * 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.alias; + +import org.elasticsearch.ElasticsearchGenerationException; +import org.elasticsearch.ElasticsearchIllegalArgumentException; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Streamable; +import org.elasticsearch.common.xcontent.*; +import org.elasticsearch.index.query.FilterBuilder; + +import java.io.IOException; +import java.util.Map; + +/** + * Represents an alias, to be associated with an index + */ +public class Alias implements Streamable { + + private String name; + + @Nullable + private String filter; + + @Nullable + private String indexRouting; + + @Nullable + private String searchRouting; + + private Alias() { + + } + + public Alias(String name) { + this.name = name; + } + + public Alias(String name, String filter) { + this.name = name; + this.filter = filter; + } + + /** + * Returns the alias name + */ + public String name() { + return name; + } + + /** + * Returns the filter associated with the alias + */ + public String filter() { + return filter; + } + + /** + * Associates a filter to the alias + */ + public Alias filter(String filter) { + this.filter = filter; + return this; + } + + /** + * Associates a filter to the alias + */ + public Alias filter(Map filter) { + if (filter == null || filter.isEmpty()) { + this.filter = null; + return this; + } + try { + XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + builder.map(filter); + this.filter = builder.string(); + return this; + } catch (IOException e) { + throw new ElasticsearchGenerationException("Failed to generate [" + filter + "]", e); + } + } + + /** + * Associates a filter to the alias + */ + public Alias filter(FilterBuilder filterBuilder) { + if (filterBuilder == null) { + this.filter = null; + return this; + } + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + filterBuilder.toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.close(); + this.filter = builder.string(); + return this; + } catch (IOException e) { + throw new ElasticsearchGenerationException("Failed to build json for alias request", e); + } + } + + /** + * Associates a routing value to the alias + */ + public Alias routing(String routing) { + this.indexRouting = routing; + this.searchRouting = routing; + return this; + } + + /** + * Returns the index routing value associated with the alias + */ + public String indexRouting() { + return indexRouting; + } + + /** + * Associates an index routing value to the alias + */ + public Alias indexRouting(String indexRouting) { + this.indexRouting = indexRouting; + return this; + } + + /** + * Returns the search routing value associated with the alias + */ + public String searchRouting() { + return searchRouting; + } + + /** + * Associates a search routing value to the alias + */ + public Alias searchRouting(String searchRouting) { + this.searchRouting = searchRouting; + return this; + } + + /** + * Allows to read an alias from the provided input stream + */ + public static Alias read(StreamInput in) throws IOException { + Alias alias = new Alias(); + alias.readFrom(in); + return alias; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + name = in.readString(); + filter = in.readOptionalString(); + indexRouting = in.readOptionalString(); + searchRouting = in.readOptionalString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(name); + out.writeOptionalString(filter); + out.writeOptionalString(indexRouting); + out.writeOptionalString(searchRouting); + } + + /** + * Parses an alias and returns its parsed representation + */ + public static Alias fromXContent(XContentParser parser) throws IOException { + Alias alias = new Alias(parser.currentName()); + + String currentFieldName = null; + XContentParser.Token token = parser.nextToken(); + if (token == null) { + throw new ElasticsearchIllegalArgumentException("No alias is specified"); + } + 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 ("filter".equals(currentFieldName)) { + Map filter = parser.mapOrdered(); + alias.filter(filter); + } + } else if (token == XContentParser.Token.VALUE_STRING) { + if ("routing".equals(currentFieldName)) { + alias.routing(parser.text()); + } else if ("index_routing".equals(currentFieldName) || "indexRouting".equals(currentFieldName) || "index-routing".equals(currentFieldName)) { + alias.indexRouting(parser.text()); + } else if ("search_routing".equals(currentFieldName) || "searchRouting".equals(currentFieldName) || "search-routing".equals(currentFieldName)) { + alias.searchRouting(parser.text()); + } + } + } + return alias; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Alias alias = (Alias) o; + + if (name != null ? !name.equals(alias.name) : alias.name != null) return false; + + return true; + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } +} diff --git a/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java index 99d3d107f08..958841aab14 100644 --- a/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java +++ b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java @@ -21,6 +21,7 @@ package org.elasticsearch.action.admin.indices.create; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest; import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -44,11 +45,13 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ private Settings settings = ImmutableSettings.Builder.EMPTY_SETTINGS; - private Map mappings = Maps.newHashMap(); + private final Map mappings = Maps.newHashMap(); - private Map customs = newHashMap(); + private final Set aliases = Sets.newHashSet(); - private Set blocks = Sets.newHashSet(); + private final Map customs = newHashMap(); + + private final Set blocks = Sets.newHashSet(); CreateIndexClusterStateUpdateRequest(String cause, String index) { @@ -66,6 +69,11 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ return this; } + public CreateIndexClusterStateUpdateRequest aliases(Set aliases) { + this.aliases.addAll(aliases); + return this; + } + public CreateIndexClusterStateUpdateRequest customs(Map customs) { this.customs.putAll(customs); return this; @@ -101,6 +109,10 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ return mappings; } + public Set aliases() { + return aliases; + } + public Map customs() { return customs; } diff --git a/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java index 665dff37747..aae996b7fca 100644 --- a/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java +++ b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java @@ -20,10 +20,13 @@ package org.elasticsearch.action.admin.indices.create; import com.google.common.base.Charsets; +import com.google.common.collect.Sets; import org.elasticsearch.ElasticsearchGenerationException; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -34,12 +37,11 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.*; import java.io.IOException; import java.util.Map; +import java.util.Set; import static com.google.common.collect.Maps.newHashMap; import static org.elasticsearch.action.ValidateActions.addValidationError; @@ -64,9 +66,11 @@ public class CreateIndexRequest extends AcknowledgedRequest private Settings settings = EMPTY_SETTINGS; - private Map mappings = newHashMap(); + private final Map mappings = newHashMap(); - private Map customs = newHashMap(); + private final Set aliases = Sets.newHashSet(); + + private final Map customs = newHashMap(); CreateIndexRequest() { } @@ -244,6 +248,56 @@ public class CreateIndexRequest extends AcknowledgedRequest return this; } + /** + * Sets the aliases that will be associated with the index when it gets created + */ + @SuppressWarnings("unchecked") + public CreateIndexRequest aliases(Map source) { + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.map(source); + return aliases(builder.string()); + } catch (IOException e) { + throw new ElasticsearchGenerationException("Failed to generate [" + source + "]", e); + } + } + + /** + * Sets the aliases that will be associated with the index when it gets created + */ + public CreateIndexRequest aliases(XContentBuilder source) { + try { + return aliases(source.string()); + } catch (IOException e) { + throw new ElasticsearchIllegalArgumentException("Failed to build json for aliases", e); + } + } + + /** + * Sets the aliases that will be associated with the index when it gets created + */ + public CreateIndexRequest aliases(String source) { + try { + XContentParser parser = XContentHelper.createParser(new BytesArray(source)); + //move to the first alias + parser.nextToken(); + while ((parser.nextToken()) != XContentParser.Token.END_OBJECT) { + alias(Alias.fromXContent(parser)); + } + return this; + } catch(IOException e) { + throw new ElasticsearchParseException("Failed to parse aliases", e); + } + } + + /** + * Adds an alias that will be associated with the index when it gets created + */ + public CreateIndexRequest alias(Alias alias) { + this.aliases.add(alias); + return this; + } + /** * Sets the settings and mappings as a single source. */ @@ -306,6 +360,9 @@ public class CreateIndexRequest extends AcknowledgedRequest for (Map.Entry entry1 : mappings.entrySet()) { mapping(entry1.getKey(), (Map) entry1.getValue()); } + } else if (name.equals("aliases")) { + found = true; + aliases((Map) entry.getValue()); } else { // maybe custom? IndexMetaData.Custom.Factory factory = IndexMetaData.lookupFactory(name); @@ -330,6 +387,10 @@ public class CreateIndexRequest extends AcknowledgedRequest return this.mappings; } + Set aliases() { + return this.aliases; + } + /** * Adds custom metadata to the index to be created. */ @@ -359,6 +420,12 @@ public class CreateIndexRequest extends AcknowledgedRequest IndexMetaData.Custom customIndexMetaData = IndexMetaData.lookupFactorySafe(type).readFrom(in); customs.put(type, customIndexMetaData); } + if (in.getVersion().onOrAfter(Version.V_1_1_0)) { + int aliasesSize = in.readVInt(); + for (int i = 0; i < aliasesSize; i++) { + aliases.add(Alias.read(in)); + } + } } @Override @@ -378,5 +445,11 @@ public class CreateIndexRequest extends AcknowledgedRequest out.writeString(entry.getKey()); IndexMetaData.lookupFactorySafe(entry.getKey()).writeTo(entry.getValue(), out); } + if (out.getVersion().onOrAfter(Version.V_1_1_0)) { + out.writeVInt(aliases.size()); + for (Alias alias : aliases) { + alias.writeTo(out); + } + } } } diff --git a/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestBuilder.java b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestBuilder.java index d3a9ae88e09..e738ec3c855 100644 --- a/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestBuilder.java +++ b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestBuilder.java @@ -20,6 +20,7 @@ package org.elasticsearch.action.admin.indices.create; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder; import org.elasticsearch.client.IndicesAdminClient; import org.elasticsearch.client.internal.InternalIndicesAdminClient; @@ -149,6 +150,38 @@ public class CreateIndexRequestBuilder extends AcknowledgedRequestBuilder templates = findTemplates(request, currentState); @@ -333,6 +342,14 @@ public class MetaDataCreateIndexService extends AbstractComponent { throw new MapperParsingException("mapping [" + entry.getKey() + "]", e); } } + + IndexQueryParserService indexQueryParserService = indexService.queryParserService(); + for (Alias alias : request.aliases()) { + if (Strings.hasLength(alias.filter())) { + aliasValidator.validateAliasFilter(alias.name(), alias.filter(), indexQueryParserService); + } + } + // now, update the mappings with the actual source Map mappingsMetaData = Maps.newHashMap(); for (DocumentMapper mapper : mapperService) { @@ -344,6 +361,11 @@ public class MetaDataCreateIndexService extends AbstractComponent { for (MappingMetaData mappingMd : mappingsMetaData.values()) { indexMetaDataBuilder.putMapping(mappingMd); } + for (Alias alias : request.aliases()) { + AliasMetaData aliasMetaData = AliasMetaData.builder(alias.name()).filter(alias.filter()) + .indexRouting(alias.indexRouting()).searchRouting(alias.searchRouting()).build(); + indexMetaDataBuilder.putAlias(aliasMetaData); + } for (Map.Entry customEntry : customs.entrySet()) { indexMetaDataBuilder.putCustom(customEntry.getKey(), customEntry.getValue()); } diff --git a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java index b8169ed93f6..d298e856677 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java @@ -22,7 +22,6 @@ package org.elasticsearch.cluster.metadata; import com.carrotsearch.hppc.cursors.ObjectCursor; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest; import org.elasticsearch.cluster.AckedClusterStateUpdateTask; import org.elasticsearch.cluster.ClusterService; @@ -37,15 +36,11 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.Index; import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.service.IndexService; import org.elasticsearch.indices.IndexMissingException; import org.elasticsearch.indices.IndicesService; -import org.elasticsearch.indices.InvalidAliasNameException; import java.util.List; import java.util.Map; @@ -59,11 +54,14 @@ public class MetaDataIndexAliasesService extends AbstractComponent { private final IndicesService indicesService; + private final AliasValidator aliasValidator; + @Inject - public MetaDataIndexAliasesService(Settings settings, ClusterService clusterService, IndicesService indicesService) { + public MetaDataIndexAliasesService(Settings settings, ClusterService clusterService, IndicesService indicesService, AliasValidator aliasValidator) { super(settings); this.clusterService = clusterService; this.indicesService = indicesService; + this.aliasValidator = aliasValidator; } public void indicesAliases(final IndicesAliasesClusterStateUpdateRequest request, final ClusterStateUpdateListener listener) { @@ -105,18 +103,10 @@ public class MetaDataIndexAliasesService extends AbstractComponent { Map indices = Maps.newHashMap(); try { for (AliasAction aliasAction : request.actions()) { - if (!Strings.hasText(aliasAction.alias()) || !Strings.hasText(aliasAction.index())) { - throw new ElasticsearchIllegalArgumentException("Index name and alias name are required"); - } + aliasValidator.validateAliasAction(aliasAction, currentState.metaData()); if (!currentState.metaData().hasIndex(aliasAction.index())) { throw new IndexMissingException(new Index(aliasAction.index())); } - if (currentState.metaData().hasIndex(aliasAction.alias())) { - throw new InvalidAliasNameException(new Index(aliasAction.index()), aliasAction.alias(), "an index exists with the same name as the alias"); - } - if (aliasAction.indexRouting() != null && aliasAction.indexRouting().indexOf(',') != -1) { - throw new ElasticsearchIllegalArgumentException("alias [" + aliasAction.alias() + "] has several routing values associated with it"); - } } boolean changed = false; @@ -155,18 +145,7 @@ public class MetaDataIndexAliasesService extends AbstractComponent { indices.put(indexMetaData.index(), indexService); } - // now, parse the filter - IndexQueryParserService indexQueryParser = indexService.queryParserService(); - try { - XContentParser parser = XContentFactory.xContent(filter).createParser(filter); - try { - indexQueryParser.parseInnerFilter(parser); - } finally { - parser.close(); - } - } catch (Throwable e) { - throw new ElasticsearchIllegalArgumentException("failed to parse filter for [" + aliasAction.alias() + "]", e); - } + aliasValidator.validateAliasFilter(aliasAction.alias(), filter, indexService.queryParserService()); } AliasMetaData newAliasMd = AliasMetaData.newAliasMetaDataBuilder( aliasAction.alias()) diff --git a/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java b/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java index 3b7cd279911..cd31781318a 100644 --- a/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java +++ b/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java @@ -19,7 +19,9 @@ package org.elasticsearch.aliases; +import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistResponse; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; import org.elasticsearch.action.index.IndexResponse; @@ -33,6 +35,7 @@ import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.query.FilterBuilder; +import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.indices.IndexMissingException; import org.elasticsearch.rest.action.admin.indices.alias.delete.AliasesMissingException; @@ -819,6 +822,72 @@ public class IndexAliasesTests extends ElasticsearchIntegrationTest { assertThat(response.getAliases(), hasKey("index1")); } + @Test + public void testCreateIndexWithAliases() throws Exception { + assertAcked(prepareCreate("test").addAlias(new Alias("alias1")) + .addAlias(new Alias("alias2").filter(FilterBuilders.missingFilter("field"))) + .addAlias(new Alias("alias3").indexRouting("index").searchRouting("search"))); + + checkAliases(); + } + + @Test + public void testCreateIndexWithAliasesInSource() throws Exception { + assertAcked(prepareCreate("test").setSource("{\n" + + " \"aliases\" : {\n" + + " \"alias1\" : {},\n" + + " \"alias2\" : {\"filter\" : {\"term\": {\"field\":\"value\"}}},\n" + + " \"alias3\" : { \"index_routing\" : \"index\", \"search_routing\" : \"search\"}\n" + + " }\n" + + "}")); + + checkAliases(); + } + + @Test + public void testCreateIndexWithAliasesSource() throws Exception { + assertAcked(prepareCreate("test").setAliases("{\n" + + " \"alias1\" : {},\n" + + " \"alias2\" : {\"filter\" : {\"term\": {\"field\":\"value\"}}},\n" + + " \"alias3\" : { \"index_routing\" : \"index\", \"search_routing\" : \"search\"}\n" + + "}")); + + checkAliases(); + } + + @Test (expected = ElasticsearchIllegalArgumentException.class) + public void testCreateIndexWithAliasesFilterNotValid() { + prepareCreate("test").addAlias(new Alias("alias1")) + .addAlias(new Alias("alias2").filter("f")) + .addAlias(new Alias("alias3").indexRouting("index").searchRouting("search")).get(); + } + + private void checkAliases() { + GetAliasesResponse getAliasesResponse = admin().indices().prepareGetAliases("alias1").get(); + assertThat(getAliasesResponse.getAliases().get("test").size(), equalTo(1)); + AliasMetaData aliasMetaData = getAliasesResponse.getAliases().get("test").get(0); + assertThat(aliasMetaData.alias(), equalTo("alias1")); + assertThat(aliasMetaData.filter(), nullValue()); + assertThat(aliasMetaData.indexRouting(), nullValue()); + assertThat(aliasMetaData.searchRouting(), nullValue()); + + getAliasesResponse = admin().indices().prepareGetAliases("alias2").get(); + assertThat(getAliasesResponse.getAliases().get("test").size(), equalTo(1)); + aliasMetaData = getAliasesResponse.getAliases().get("test").get(0); + assertThat(aliasMetaData.alias(), equalTo("alias2")); + assertThat(aliasMetaData.filter(), notNullValue()); + assertThat(aliasMetaData.indexRouting(), nullValue()); + assertThat(aliasMetaData.searchRouting(), nullValue()); + + getAliasesResponse = admin().indices().prepareGetAliases("alias3").get(); + assertThat(getAliasesResponse.getAliases().get("test").size(), equalTo(1)); + aliasMetaData = getAliasesResponse.getAliases().get("test").get(0); + assertThat(aliasMetaData.alias(), equalTo("alias3")); + assertThat(aliasMetaData.filter(), nullValue()); + assertThat(aliasMetaData.indexRouting(), equalTo("index")); + assertThat(aliasMetaData.searchRouting(), equalTo("search")); + } + private void assertHits(SearchHits hits, String... ids) { assertThat(hits.totalHits(), equalTo((long) ids.length)); Set hitIds = newHashSet();