Merge pull request #15371 from jimferenczi/alias_routing

Resolves the conflict between alias routing and parent routing by applying the alias routing and ignoring the parent routing.
This commit is contained in:
Jim Ferenczi 2015-12-21 09:58:45 +01:00
commit 1ec44dcdda
20 changed files with 180 additions and 57 deletions

View File

@ -62,4 +62,12 @@ public interface DocumentRequest<T> extends IndicesRequest {
* @return the Routing
*/
String routing();
/**
* Get the parent for this request
* @return the Parent
*/
String parent();
}

View File

@ -239,7 +239,7 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
}
} else {
concreteIndices.resolveIfAbsent(req);
req.routing(clusterState.metaData().resolveIndexRouting(req.routing(), req.index()));
req.routing(clusterState.metaData().resolveIndexRouting(req.parent(), req.routing(), req.index()));
}
}
}

View File

@ -50,6 +50,8 @@ public class DeleteRequest extends ReplicationRequest<DeleteRequest> implements
private String id;
@Nullable
private String routing;
@Nullable
private String parent;
private boolean refresh;
private long version = Versions.MATCH_ANY;
private VersionType versionType = VersionType.INTERNAL;
@ -94,6 +96,7 @@ public class DeleteRequest extends ReplicationRequest<DeleteRequest> implements
this.type = request.type();
this.id = request.id();
this.routing = request.routing();
this.parent = request.parent();
this.refresh = request.refresh();
this.version = request.version();
this.versionType = request.versionType();
@ -155,13 +158,18 @@ public class DeleteRequest extends ReplicationRequest<DeleteRequest> implements
}
/**
* Sets the parent id of this document. Will simply set the routing to this value, as it is only
* used for routing with delete requests.
* @return The parent for this request.
*/
@Override
public String parent() {
return parent;
}
/**
* Sets the parent id of this document.
*/
public DeleteRequest parent(String parent) {
if (routing == null) {
routing = parent;
}
this.parent = parent;
return this;
}
@ -230,6 +238,7 @@ public class DeleteRequest extends ReplicationRequest<DeleteRequest> implements
type = in.readString();
id = in.readString();
routing = in.readOptionalString();
parent = in.readOptionalString();
refresh = in.readBoolean();
version = in.readLong();
versionType = VersionType.fromValue(in.readByte());
@ -241,6 +250,7 @@ public class DeleteRequest extends ReplicationRequest<DeleteRequest> implements
out.writeString(type);
out.writeString(id);
out.writeOptionalString(routing());
out.writeOptionalString(parent());
out.writeBoolean(refresh);
out.writeLong(version);
out.writeByte(versionType.getValue());

View File

@ -95,7 +95,7 @@ public class TransportDeleteAction extends TransportReplicationAction<DeleteRequ
@Override
protected void resolveRequest(final MetaData metaData, String concreteIndex, DeleteRequest request) {
request.routing(metaData.resolveIndexRouting(request.routing(), request.index()));
request.routing(metaData.resolveIndexRouting(request.parent(), request.routing(), request.index()));
if (metaData.hasIndex(concreteIndex)) {
// check if routing is required, if so, do a broadcast delete
MappingMetaData mappingMd = metaData.index(concreteIndex).mappingOrDefault(request.type());

View File

@ -49,6 +49,7 @@ public class GetRequest extends SingleShardRequest<GetRequest> implements Realti
private String type;
private String id;
private String routing;
private String parent;
private String preference;
private String[] fields;
@ -77,6 +78,7 @@ public class GetRequest extends SingleShardRequest<GetRequest> implements Realti
this.type = getRequest.type;
this.id = getRequest.id;
this.routing = getRequest.routing;
this.parent = getRequest.parent;
this.preference = getRequest.preference;
this.fields = getRequest.fields;
this.fetchSourceContext = getRequest.fetchSourceContext;
@ -153,13 +155,17 @@ public class GetRequest extends SingleShardRequest<GetRequest> implements Realti
}
/**
* Sets the parent id of this document. Will simply set the routing to this value, as it is only
* used for routing with delete requests.
* @return The parent for this request.
*/
public String parent() {
return parent;
}
/**
* Sets the parent id of this document.
*/
public GetRequest parent(String parent) {
if (routing == null) {
routing = parent;
}
this.parent = parent;
return this;
}
@ -291,6 +297,7 @@ public class GetRequest extends SingleShardRequest<GetRequest> implements Realti
type = in.readString();
id = in.readString();
routing = in.readOptionalString();
parent = in.readOptionalString();
preference = in.readOptionalString();
refresh = in.readBoolean();
int size = in.readInt();
@ -320,6 +327,7 @@ public class GetRequest extends SingleShardRequest<GetRequest> implements Realti
out.writeString(type);
out.writeString(id);
out.writeOptionalString(routing);
out.writeOptionalString(parent);
out.writeOptionalString(preference);
out.writeBoolean(refresh);

View File

@ -57,6 +57,7 @@ public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements I
private String type;
private String id;
private String routing;
private String parent;
private String[] fields;
private long version = Versions.MATCH_ANY;
private VersionType versionType = VersionType.INTERNAL;
@ -124,12 +125,17 @@ public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements I
}
public Item parent(String parent) {
if (routing == null) {
this.routing = parent;
}
this.parent = parent;
return this;
}
/**
* @return The parent for this request.
*/
public String parent() {
return parent;
}
public Item fields(String... fields) {
this.fields = fields;
return this;
@ -181,6 +187,7 @@ public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements I
type = in.readOptionalString();
id = in.readString();
routing = in.readOptionalString();
parent = in.readOptionalString();
int size = in.readVInt();
if (size > 0) {
fields = new String[size];
@ -200,6 +207,7 @@ public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements I
out.writeOptionalString(type);
out.writeString(id);
out.writeOptionalString(routing);
out.writeOptionalString(parent);
if (fields == null) {
out.writeVInt(0);
} else {
@ -229,6 +237,7 @@ public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements I
if (!id.equals(item.id)) return false;
if (!index.equals(item.index)) return false;
if (routing != null ? !routing.equals(item.routing) : item.routing != null) return false;
if (parent != null ? !parent.equals(item.parent) : item.parent != null) return false;
if (type != null ? !type.equals(item.type) : item.type != null) return false;
if (versionType != item.versionType) return false;
@ -241,6 +250,7 @@ public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements I
result = 31 * result + (type != null ? type.hashCode() : 0);
result = 31 * result + id.hashCode();
result = 31 * result + (routing != null ? routing.hashCode() : 0);
result = 31 * result + (parent != null ? parent.hashCode() : 0);
result = 31 * result + (fields != null ? Arrays.hashCode(fields) : 0);
result = 31 * result + Long.hashCode(version);
result = 31 * result + versionType.hashCode();

View File

@ -82,7 +82,7 @@ public class TransportGetAction extends TransportSingleShardAction<GetRequest, G
request.request().preference(Preference.PRIMARY.type());
}
// update the routing (request#index here is possibly an alias)
request.request().routing(state.metaData().resolveIndexRouting(request.request().routing(), request.request().index()));
request.request().routing(state.metaData().resolveIndexRouting(request.request().parent(), request.request().routing(), request.request().index()));
// Fail fast on the node that received the request.
if (request.request().routing() == null && state.getMetaData().routingRequired(request.concreteIndex(), request.request().type())) {
throw new RoutingMissingException(request.concreteIndex(), request.request().type(), request.request().id());

View File

@ -68,7 +68,7 @@ public class TransportMultiGetAction extends HandledTransportAction<MultiGetRequ
responses.set(i, new MultiGetItemResponse(null, new MultiGetResponse.Failure(item.index(), item.type(), item.id(), new IndexNotFoundException(item.index()))));
continue;
}
item.routing(clusterState.metaData().resolveIndexRouting(item.routing(), item.index()));
item.routing(clusterState.metaData().resolveIndexRouting(item.parent(), item.routing(), item.index()));
String concreteSingleIndex = indexNameExpressionResolver.concreteSingleIndex(clusterState, item);
if (item.routing() == null && clusterState.getMetaData().routingRequired(concreteSingleIndex, item.type())) {
responses.set(i, new MultiGetItemResponse(null, new MultiGetResponse.Failure(concreteSingleIndex, item.type(), item.id(),

View File

@ -312,14 +312,10 @@ public class IndexRequest extends ReplicationRequest<IndexRequest> implements Do
}
/**
* Sets the parent id of this document. If routing is not set, automatically set it as the
* routing as well.
* Sets the parent id of this document.
*/
public IndexRequest parent(String parent) {
this.parent = parent;
if (routing == null) {
routing = parent;
}
return this;
}
@ -601,7 +597,7 @@ public class IndexRequest extends ReplicationRequest<IndexRequest> implements Do
public void process(MetaData metaData, @Nullable MappingMetaData mappingMd, boolean allowIdGeneration, String concreteIndex) {
// resolve the routing if needed
routing(metaData.resolveIndexRouting(routing, index));
routing(metaData.resolveIndexRouting(parent, routing, index));
// resolve timestamp if provided externally
if (timestamp != null) {

View File

@ -65,6 +65,8 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
private String routing;
private String parent;
private VersionType versionType = VersionType.INTERNAL;
private long version = Versions.MATCH_ANY;
@ -162,6 +164,7 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
this.flagsEnum = other.getFlags().clone();
this.preference = other.preference();
this.routing = other.routing();
this.parent = other.parent();
if (other.selectedFields != null) {
this.selectedFields = new HashSet<>(other.selectedFields);
}
@ -181,6 +184,7 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
this.type = item.type();
this.selectedFields(item.fields());
this.routing(item.routing());
this.parent(item.parent());
}
public EnumSet<Flag> getFlags() {
@ -259,14 +263,16 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
return this;
}
@Override
public String parent() {
return parent;
}
/**
* Sets the parent id of this document. Will simply set the routing to this
* value, as it is only used for routing with delete requests.
* Sets the parent id of this document.
*/
public TermVectorsRequest parent(String parent) {
if (routing == null) {
routing = parent;
}
this.parent = parent;
return this;
}
@ -506,6 +512,7 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
doc = in.readBytesReference();
}
routing = in.readOptionalString();
parent = in.readOptionalString();
preference = in.readOptionalString();
long flags = in.readVLong();
@ -545,6 +552,7 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
out.writeBytesReference(doc);
}
out.writeOptionalString(routing);
out.writeOptionalString(parent);
out.writeOptionalString(preference);
long longFlags = 0;
for (Flag flag : flagsEnum) {
@ -629,6 +637,8 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
termVectorsRequest.doc(jsonBuilder().copyCurrentStructure(parser));
} else if ("_routing".equals(currentFieldName) || "routing".equals(currentFieldName)) {
termVectorsRequest.routing = parser.text();
} else if ("_parent".equals(currentFieldName) || "parent".equals(currentFieldName)) {
termVectorsRequest.parent = parser.text();
} else if ("_version".equals(currentFieldName) || "version".equals(currentFieldName)) {
termVectorsRequest.version = parser.longValue();
} else if ("_version_type".equals(currentFieldName) || "_versionType".equals(currentFieldName) || "version_type".equals(currentFieldName) || "versionType".equals(currentFieldName)) {

View File

@ -66,7 +66,7 @@ public class TransportMultiTermVectorsAction extends HandledTransportAction<Mult
for (int i = 0; i < request.requests.size(); i++) {
TermVectorsRequest termVectorsRequest = request.requests.get(i);
termVectorsRequest.startTime = System.currentTimeMillis();
termVectorsRequest.routing(clusterState.metaData().resolveIndexRouting(termVectorsRequest.routing(), termVectorsRequest.index()));
termVectorsRequest.routing(clusterState.metaData().resolveIndexRouting(termVectorsRequest.parent(), termVectorsRequest.routing(), termVectorsRequest.index()));
if (!clusterState.metaData().hasConcreteIndex(termVectorsRequest.index())) {
responses.set(i, new MultiTermVectorsItemResponse(null, new MultiTermVectorsResponse.Failure(termVectorsRequest.index(),
termVectorsRequest.type(), termVectorsRequest.id(), new IndexNotFoundException(termVectorsRequest.index()))));
@ -88,12 +88,12 @@ public class TransportMultiTermVectorsAction extends HandledTransportAction<Mult
}
shardRequest.add(i, termVectorsRequest);
}
if (shardRequests.size() == 0) {
// only failures..
listener.onResponse(new MultiTermVectorsResponse(responses.toArray(new MultiTermVectorsItemResponse[responses.length()])));
}
final AtomicInteger counter = new AtomicInteger(shardRequests.size());
for (final MultiTermVectorsShardRequest shardRequest : shardRequests.values()) {
shardAction.execute(shardRequest, new ActionListener<MultiTermVectorsShardResponse>() {

View File

@ -71,8 +71,8 @@ public class TransportTermVectorsAction extends TransportSingleShardAction<TermV
@Override
protected void resolveRequest(ClusterState state, InternalRequest request) {
// update the routing (request#index here is possibly an alias)
request.request().routing(state.metaData().resolveIndexRouting(request.request().routing(), request.request().index()));
// update the routing (request#index here is possibly an alias or a parent)
request.request().routing(state.metaData().resolveIndexRouting(request.request().parent(), request.request().routing(), request.request().index()));
// Fail fast on the node that received the request.
if (request.request().routing() == null && state.getMetaData().routingRequired(request.concreteIndex(), request.request().type())) {
throw new RoutingMissingException(request.concreteIndex(), request.request().type(), request.request().id());

View File

@ -101,7 +101,7 @@ public class TransportUpdateAction extends TransportInstanceSingleOperationActio
@Override
protected boolean resolveRequest(ClusterState state, UpdateRequest request, ActionListener<UpdateResponse> listener) {
request.routing((state.metaData().resolveIndexRouting(request.routing(), request.index())));
request.routing((state.metaData().resolveIndexRouting(request.parent(), request.routing(), request.index())));
// Fail fast on the node that received the request, rather than failing when translating on the index or delete request.
if (request.routing() == null && state.getMetaData().routingRequired(request.concreteIndex(), request.type())) {
throw new RoutingMissingException(request.concreteIndex(), request.type(), request.id());

View File

@ -184,13 +184,10 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
}
/**
* The parent id is used for the upsert request and also implicitely sets the routing if not already set.
* The parent id is used for the upsert request.
*/
public UpdateRequest parent(String parent) {
this.parent = parent;
if (routing == null) {
routing = parent;
}
return this;
}

View File

@ -440,13 +440,19 @@ public class MetaData implements Iterable<IndexMetaData>, Diffable<MetaData>, Fr
*/
// TODO: This can be moved to IndexNameExpressionResolver too, but this means that we will support wildcards and other expressions
// in the index,bulk,update and delete apis.
public String resolveIndexRouting(@Nullable String routing, String aliasOrIndex) {
public String resolveIndexRouting(@Nullable String parent, @Nullable String routing, String aliasOrIndex) {
if (aliasOrIndex == null) {
if (routing == null) {
return parent;
}
return routing;
}
AliasOrIndex result = getAliasAndIndexLookup().get(aliasOrIndex);
if (result == null || result.isAlias() == false) {
if (routing == null) {
return parent;
}
return routing;
}
AliasOrIndex.Alias alias = (AliasOrIndex.Alias) result;
@ -460,17 +466,19 @@ public class MetaData implements Iterable<IndexMetaData>, Diffable<MetaData>, Fr
}
AliasMetaData aliasMd = alias.getFirstAliasMetaData();
if (aliasMd.indexRouting() != null) {
if (aliasMd.indexRouting().indexOf(',') != -1) {
throw new IllegalArgumentException("index/alias [" + aliasOrIndex + "] provided with routing value [" + aliasMd.getIndexRouting() + "] that resolved to several routing values, rejecting operation");
}
if (routing != null) {
if (!routing.equals(aliasMd.indexRouting())) {
throw new IllegalArgumentException("Alias [" + aliasOrIndex + "] has index routing associated with it [" + aliasMd.indexRouting() + "], and was provided with routing value [" + routing + "], rejecting operation");
}
}
routing = aliasMd.indexRouting();
// Alias routing overrides the parent routing (if any).
return aliasMd.indexRouting();
}
if (routing != null) {
if (routing.indexOf(',') != -1) {
throw new IllegalArgumentException("index/alias [" + aliasOrIndex + "] provided with routing value [" + routing + "] that resolved to several routing values, rejecting operation");
}
if (routing == null) {
return parent;
}
return routing;
}

View File

@ -255,7 +255,7 @@ public class TermVectorsUnitTests extends ESTestCase {
assertThat(request.positions(), equalTo(req2.positions()));
assertThat(request.termStatistics(), equalTo(req2.termStatistics()));
assertThat(request.preference(), equalTo(pref));
assertThat(request.routing(), equalTo(parent));
assertThat(request.routing(), equalTo(null));
}
}

View File

@ -24,6 +24,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
public class MetaDataTests extends ESTestCase {
@ -41,4 +42,72 @@ public class MetaDataTests extends ESTestCase {
}
}
public void testResolveIndexRouting() {
IndexMetaData.Builder builder = IndexMetaData.builder("index")
.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT))
.numberOfShards(1)
.numberOfReplicas(0)
.putAlias(AliasMetaData.builder("alias0").build())
.putAlias(AliasMetaData.builder("alias1").routing("1").build())
.putAlias(AliasMetaData.builder("alias2").routing("1,2").build());
MetaData metaData = MetaData.builder().put(builder).build();
// no alias, no index
assertEquals(metaData.resolveIndexRouting(null, null, null), null);
assertEquals(metaData.resolveIndexRouting(null, "0", null), "0");
assertEquals(metaData.resolveIndexRouting("32", "0", null), "0");
assertEquals(metaData.resolveIndexRouting("32", null, null), "32");
// index, no alias
assertEquals(metaData.resolveIndexRouting("32", "0", "index"), "0");
assertEquals(metaData.resolveIndexRouting("32", null, "index"), "32");
assertEquals(metaData.resolveIndexRouting(null, null, "index"), null);
assertEquals(metaData.resolveIndexRouting(null, "0", "index"), "0");
// alias with no index routing
assertEquals(metaData.resolveIndexRouting(null, null, "alias0"), null);
assertEquals(metaData.resolveIndexRouting(null, "0", "alias0"), "0");
assertEquals(metaData.resolveIndexRouting("32", null, "alias0"), "32");
assertEquals(metaData.resolveIndexRouting("32", "0", "alias0"), "0");
// alias with index routing.
assertEquals(metaData.resolveIndexRouting(null, null, "alias1"), "1");
assertEquals(metaData.resolveIndexRouting("32", null, "alias1"), "1");
assertEquals(metaData.resolveIndexRouting("32", "1", "alias1"), "1");
try {
metaData.resolveIndexRouting(null, "0", "alias1");
fail("should fail");
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), is("Alias [alias1] has index routing associated with it [1], and was provided with routing value [0], rejecting operation"));
}
try {
metaData.resolveIndexRouting("32", "0", "alias1");
fail("should fail");
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), is("Alias [alias1] has index routing associated with it [1], and was provided with routing value [0], rejecting operation"));
}
// alias with invalid index routing.
try {
metaData.resolveIndexRouting(null, null, "alias2");
fail("should fail");
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), is("index/alias [alias2] provided with routing value [1,2] that resolved to several routing values, rejecting operation"));
}
try {
metaData.resolveIndexRouting(null, "1", "alias2");
fail("should fail");
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), is("index/alias [alias2] provided with routing value [1,2] that resolved to several routing values, rejecting operation"));
}
try {
metaData.resolveIndexRouting("32", null, "alias2");
fail("should fail");
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), is("index/alias [alias2] provided with routing value [1,2] that resolved to several routing values, rejecting operation"));
}
}
}

View File

@ -51,24 +51,27 @@ public class AliasResolveRoutingIT extends ESIntegTestCase {
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias0").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias0").routing("0")).execute().actionGet();
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "test1"), nullValue());
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "alias"), nullValue());
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "test1"), nullValue());
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "alias"), nullValue());
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "test1"), nullValue());
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "alias10"), equalTo("0"));
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "alias20"), equalTo("0"));
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "alias21"), equalTo("1"));
assertThat(clusterService().state().metaData().resolveIndexRouting("3", "test1"), equalTo("3"));
assertThat(clusterService().state().metaData().resolveIndexRouting("0", "alias10"), equalTo("0"));
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "test1"), nullValue());
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "alias10"), equalTo("0"));
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "alias20"), equalTo("0"));
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "alias21"), equalTo("1"));
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "3", "test1"), equalTo("3"));
assertThat(clusterService().state().metaData().resolveIndexRouting(null, "0", "alias10"), equalTo("0"));
// Force the alias routing and ignore the parent.
assertThat(clusterService().state().metaData().resolveIndexRouting("1", null, "alias10"), equalTo("0"));
try {
clusterService().state().metaData().resolveIndexRouting("1", "alias10");
clusterService().state().metaData().resolveIndexRouting(null, "1", "alias10");
fail("should fail");
} catch (IllegalArgumentException e) {
// all is well, we can't have two mappings, one provided, and one in the alias
}
try {
clusterService().state().metaData().resolveIndexRouting(null, "alias0");
clusterService().state().metaData().resolveIndexRouting(null, null, "alias0");
fail("should fail");
} catch (IllegalArgumentException ex) {
// Expected

View File

@ -223,6 +223,7 @@ Can't be used to update the routing of an existing document.
Parent is used to route the update request to the right shard and sets the
parent for the upsert request if the document being updated doesn't exist.
Can't be used to update the `parent` of an existing document.
If an alias index routing is specified then it overrides the parent routing and it is used to route the request.
`timeout`::

View File

@ -193,8 +193,8 @@ curl -XPOST 'http://localhost:9200/_aliases' -d '
As shown in the example above, search routing may contain several values
separated by comma. Index routing can contain only a single value.
If an operation that uses routing alias also has a routing parameter, an
intersection of both alias routing and routing specified in the
If a search operation that uses routing alias also has a routing parameter, an
intersection of both search alias routing and routing specified in the
parameter is used. For example the following command will use "2" as a
routing value:
@ -203,6 +203,9 @@ routing value:
curl -XGET 'http://localhost:9200/alias2/_search?q=user:kimchy&routing=2,3'
--------------------------------------------------
If an index operation that uses index routing alias also has a parent routing, the
parent routing is ignored.
[float]
[[alias-adding]]
=== Add a single alias