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
This commit is contained in:
parent
8601529be8
commit
3afdf4a872
|
@ -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 <<create-index-aliases,index creation>>:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
curl -XPUT localhost:9200/logs_20142801 -d '{
|
||||
"aliases" : {
|
||||
"current_day" : {},
|
||||
"2014" : {
|
||||
"filter" : {
|
||||
"term" : {"year" : 2014 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}'
|
||||
--------------------------------------------------
|
||||
|
||||
[float]
|
||||
[[deleting]]
|
||||
=== Delete aliases
|
||||
|
|
|
@ -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 <<indices-aliases,aliases>>:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
curl -XPUT localhost:9200/test -d '{
|
||||
"aliases" : {
|
||||
"alias_1" : {},
|
||||
"alias_2" : {
|
||||
"filter" : {
|
||||
"term" : {"user" : "kimchy" }
|
||||
},
|
||||
"routing" : "kimchy"
|
||||
}
|
||||
}
|
||||
}'
|
||||
--------------------------------------------------
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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<String, Object> 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<String, Object> 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;
|
||||
}
|
||||
}
|
|
@ -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<String, String> mappings = Maps.newHashMap();
|
||||
private final Map<String, String> mappings = Maps.newHashMap();
|
||||
|
||||
private Map<String, IndexMetaData.Custom> customs = newHashMap();
|
||||
private final Set<Alias> aliases = Sets.newHashSet();
|
||||
|
||||
private Set<ClusterBlock> blocks = Sets.newHashSet();
|
||||
private final Map<String, IndexMetaData.Custom> customs = newHashMap();
|
||||
|
||||
private final Set<ClusterBlock> blocks = Sets.newHashSet();
|
||||
|
||||
|
||||
CreateIndexClusterStateUpdateRequest(String cause, String index) {
|
||||
|
@ -66,6 +69,11 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ
|
|||
return this;
|
||||
}
|
||||
|
||||
public CreateIndexClusterStateUpdateRequest aliases(Set<Alias> aliases) {
|
||||
this.aliases.addAll(aliases);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateIndexClusterStateUpdateRequest customs(Map<String, IndexMetaData.Custom> customs) {
|
||||
this.customs.putAll(customs);
|
||||
return this;
|
||||
|
@ -101,6 +109,10 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ
|
|||
return mappings;
|
||||
}
|
||||
|
||||
public Set<Alias> aliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
public Map<String, IndexMetaData.Custom> customs() {
|
||||
return customs;
|
||||
}
|
||||
|
|
|
@ -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<CreateIndexRequest>
|
|||
|
||||
private Settings settings = EMPTY_SETTINGS;
|
||||
|
||||
private Map<String, String> mappings = newHashMap();
|
||||
private final Map<String, String> mappings = newHashMap();
|
||||
|
||||
private Map<String, IndexMetaData.Custom> customs = newHashMap();
|
||||
private final Set<Alias> aliases = Sets.newHashSet();
|
||||
|
||||
private final Map<String, IndexMetaData.Custom> customs = newHashMap();
|
||||
|
||||
CreateIndexRequest() {
|
||||
}
|
||||
|
@ -244,6 +248,56 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
|
|||
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<CreateIndexRequest>
|
|||
for (Map.Entry<String, Object> entry1 : mappings.entrySet()) {
|
||||
mapping(entry1.getKey(), (Map<String, Object>) entry1.getValue());
|
||||
}
|
||||
} else if (name.equals("aliases")) {
|
||||
found = true;
|
||||
aliases((Map<String, Object>) entry.getValue());
|
||||
} else {
|
||||
// maybe custom?
|
||||
IndexMetaData.Custom.Factory factory = IndexMetaData.lookupFactory(name);
|
||||
|
@ -330,6 +387,10 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
|
|||
return this.mappings;
|
||||
}
|
||||
|
||||
Set<Alias> aliases() {
|
||||
return this.aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds custom metadata to the index to be created.
|
||||
*/
|
||||
|
@ -359,6 +420,12 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
|
|||
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<CreateIndexRequest>
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Create
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the aliases that will be associated with the index when it gets created
|
||||
*/
|
||||
public CreateIndexRequestBuilder setAliases(Map source) {
|
||||
request.aliases(source);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the aliases that will be associated with the index when it gets created
|
||||
*/
|
||||
public CreateIndexRequestBuilder setAliases(String source) {
|
||||
request.aliases(source);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the aliases that will be associated with the index when it gets created
|
||||
*/
|
||||
public CreateIndexRequestBuilder setAliases(XContentBuilder source) {
|
||||
request.aliases(source);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias that will be associated with the index when it gets created
|
||||
*/
|
||||
public CreateIndexRequestBuilder addAlias(Alias alias) {
|
||||
request.alias(alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the settings and mappings as a single source.
|
||||
*/
|
||||
|
|
|
@ -85,7 +85,7 @@ public class TransportCreateIndexAction extends TransportMasterNodeOperationActi
|
|||
CreateIndexClusterStateUpdateRequest updateRequest = new CreateIndexClusterStateUpdateRequest(cause, request.index())
|
||||
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
|
||||
.settings(request.settings()).mappings(request.mappings())
|
||||
.customs(request.customs());
|
||||
.aliases(request.aliases()).customs(request.customs());
|
||||
|
||||
createIndexService.createIndex(updateRequest, new ClusterStateUpdateListener() {
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 org.elasticsearch.ElasticsearchIllegalArgumentException;
|
||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.indices.InvalidAliasNameException;
|
||||
|
||||
/**
|
||||
* Validator for an alias, to be used before adding an alias to the index metadata
|
||||
* and make sure the alias is valid
|
||||
*/
|
||||
public class AliasValidator extends AbstractComponent {
|
||||
|
||||
@Inject
|
||||
public AliasValidator(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to validate an {@link org.elasticsearch.cluster.metadata.AliasAction} and make sure
|
||||
* it's valid before it gets added to the index metadata
|
||||
*/
|
||||
public void validateAliasAction(AliasAction aliasAction, MetaData metaData) {
|
||||
validateAlias(aliasAction.alias(), aliasAction.index(), aliasAction.indexRouting(), metaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to validate an {@link org.elasticsearch.action.admin.indices.alias.Alias} and make sure
|
||||
* it's valid before it gets added to the index metadata
|
||||
*/
|
||||
public void validateAlias(Alias alias, String index, MetaData metaData) {
|
||||
validateAlias(alias.name(), index, alias.indexRouting(), metaData);
|
||||
}
|
||||
|
||||
private void validateAlias(String alias, String index, String indexRouting, MetaData metaData) {
|
||||
assert metaData != null;
|
||||
if (!Strings.hasText(alias) || !Strings.hasText(index)) {
|
||||
throw new ElasticsearchIllegalArgumentException("index name and alias name are required");
|
||||
}
|
||||
|
||||
if (metaData.hasIndex(alias)) {
|
||||
throw new InvalidAliasNameException(new Index(index), alias, "an index exists with the same name as the alias");
|
||||
}
|
||||
|
||||
if (indexRouting != null && indexRouting.indexOf(',') != -1) {
|
||||
throw new ElasticsearchIllegalArgumentException("alias [" + alias + "] has several index routing values associated with it");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an alias filter by parsing it using the
|
||||
* provided {@link org.elasticsearch.index.query.IndexQueryParserService}
|
||||
* @throws org.elasticsearch.ElasticsearchIllegalArgumentException if the filter is not valid
|
||||
*/
|
||||
public void validateAliasFilter(String alias, String filter, IndexQueryParserService indexQueryParserService) {
|
||||
assert indexQueryParserService != null;
|
||||
try {
|
||||
XContentParser parser = XContentFactory.xContent(filter).createParser(filter);
|
||||
try {
|
||||
indexQueryParserService.parseInnerFilter(parser);
|
||||
} finally {
|
||||
parser.close();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new ElasticsearchIllegalArgumentException("failed to parse filter for alias [" + alias + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import org.apache.lucene.util.CollectionUtil;
|
|||
import org.apache.lucene.util.IOUtils;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest;
|
||||
import org.elasticsearch.cluster.AckedClusterStateUpdateTask;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
|
@ -59,6 +60,7 @@ import org.elasticsearch.index.Index;
|
|||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
import org.elasticsearch.index.mapper.MapperParsingException;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.service.IndexService;
|
||||
import org.elasticsearch.indices.IndexAlreadyExistsException;
|
||||
import org.elasticsearch.indices.IndicesService;
|
||||
|
@ -92,10 +94,12 @@ public class MetaDataCreateIndexService extends AbstractComponent {
|
|||
private final MetaDataService metaDataService;
|
||||
private final Version version;
|
||||
private final String riverIndexName;
|
||||
private final AliasValidator aliasValidator;
|
||||
|
||||
@Inject
|
||||
public MetaDataCreateIndexService(Settings settings, Environment environment, ThreadPool threadPool, ClusterService clusterService, IndicesService indicesService,
|
||||
AllocationService allocationService, MetaDataService metaDataService, Version version, @RiverIndexName String riverIndexName) {
|
||||
AllocationService allocationService, MetaDataService metaDataService, Version version, @RiverIndexName String riverIndexName,
|
||||
AliasValidator aliasValidator) {
|
||||
super(settings);
|
||||
this.environment = environment;
|
||||
this.threadPool = threadPool;
|
||||
|
@ -105,6 +109,7 @@ public class MetaDataCreateIndexService extends AbstractComponent {
|
|||
this.metaDataService = metaDataService;
|
||||
this.version = version;
|
||||
this.riverIndexName = riverIndexName;
|
||||
this.aliasValidator = aliasValidator;
|
||||
}
|
||||
|
||||
public void createIndex(final CreateIndexClusterStateUpdateRequest request, final ClusterStateUpdateListener listener) {
|
||||
|
@ -214,6 +219,10 @@ public class MetaDataCreateIndexService extends AbstractComponent {
|
|||
try {
|
||||
validate(request, currentState);
|
||||
|
||||
for (Alias alias : request.aliases()) {
|
||||
aliasValidator.validateAlias(alias, request.index(), currentState.metaData());
|
||||
}
|
||||
|
||||
// we only find a template when its an API call (a new index)
|
||||
// find templates, highest order are better matching
|
||||
List<IndexTemplateMetaData> 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<String, MappingMetaData> 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<String, Custom> customEntry : customs.entrySet()) {
|
||||
indexMetaDataBuilder.putCustom(customEntry.getKey(), customEntry.getValue());
|
||||
}
|
||||
|
|
|
@ -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<String, IndexService> 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())
|
||||
|
|
|
@ -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<String> hitIds = newHashSet();
|
||||
|
|
Loading…
Reference in New Issue