public void testMultipleAliasesPrecedence() throws Exception {
    client()
        .admin()
        .indices()
        .preparePutTemplate("template1")
        .setTemplate("*")
        .setOrder(0)
        .addAlias(new Alias("alias1"))
        .addAlias(new Alias("{index}-alias"))
        .addAlias(
            new Alias("alias3")
                .filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("test"))))
        .addAlias(new Alias("alias4"))
        .get();

    client()
        .admin()
        .indices()
        .preparePutTemplate("template2")
        .setTemplate("te*")
        .setOrder(1)
        .addAlias(new Alias("alias1").routing("test"))
        .addAlias(new Alias("alias3"))
        .get();

    assertAcked(
        prepareCreate("test").addAlias(new Alias("test-alias").searchRouting("test-routing")));

    ensureGreen();

    GetAliasesResponse getAliasesResponse =
        client().admin().indices().prepareGetAliases().addIndices("test").get();
    assertThat(getAliasesResponse.getAliases().get("test").size(), equalTo(4));

    for (AliasMetaData aliasMetaData : getAliasesResponse.getAliases().get("test")) {
      assertThat(
          aliasMetaData.alias(),
          anyOf(equalTo("alias1"), equalTo("test-alias"), equalTo("alias3"), equalTo("alias4")));
      if ("alias1".equals(aliasMetaData.alias())) {
        assertThat(aliasMetaData.indexRouting(), equalTo("test"));
        assertThat(aliasMetaData.searchRouting(), equalTo("test"));
      } else if ("alias3".equals(aliasMetaData.alias())) {
        assertThat(aliasMetaData.filter(), nullValue());
      } else if ("test-alias".equals(aliasMetaData.alias())) {
        assertThat(aliasMetaData.indexRouting(), nullValue());
        assertThat(aliasMetaData.searchRouting(), equalTo("test-routing"));
      }
    }
  }
    public IndexMetaData build() {
      MapBuilder<String, AliasMetaData> tmpAliases = aliases;
      Settings tmpSettings = settings;

      // For backward compatibility
      String[] legacyAliases = settings.getAsArray("index.aliases");
      if (legacyAliases.length > 0) {
        tmpAliases = MapBuilder.newMapBuilder();
        for (String alias : legacyAliases) {
          AliasMetaData aliasMd = AliasMetaData.newAliasMetaDataBuilder(alias).build();
          tmpAliases.put(alias, aliasMd);
        }
        tmpAliases.putAll(aliases.immutableMap());
        // Remove index.aliases from settings once they are migrated to the new data structure
        tmpSettings =
            ImmutableSettings.settingsBuilder().put(settings).putArray("index.aliases").build();
      }

      // update default mapping on the MappingMetaData
      if (mappings.containsKey(MapperService.DEFAULT_MAPPING)) {
        MappingMetaData defaultMapping = mappings.get(MapperService.DEFAULT_MAPPING);
        for (MappingMetaData mappingMetaData : mappings.map().values()) {
          mappingMetaData.updateDefaultMapping(defaultMapping);
        }
      }

      return new IndexMetaData(
          index,
          version,
          state,
          tmpSettings,
          mappings.immutableMap(),
          tmpAliases.immutableMap(),
          customs.immutableMap());
    }
 private Query parse(AliasMetaData alias, QueryShardContext parseContext) {
   if (alias.filter() == null) {
     return null;
   }
   try {
     byte[] filterSource = alias.filter().uncompressed();
     try (XContentParser parser =
         XContentFactory.xContent(filterSource).createParser(filterSource)) {
       ParsedQuery parsedFilter = parseContext.parseInnerFilter(parser);
       return parsedFilter == null ? null : parsedFilter.query();
     }
   } catch (IOException ex) {
     throw new AliasFilterParsingException(
         parseContext.index(), alias.getAlias(), "Invalid alias filter", ex);
   }
 }
    public static void toXContent(
        AliasMetaData aliasMetaData, XContentBuilder builder, ToXContent.Params params)
        throws IOException {
      builder.startObject(aliasMetaData.alias(), XContentBuilder.FieldCaseConversion.NONE);

      boolean binary = params.paramAsBoolean("binary", false);

      if (aliasMetaData.filter() != null) {
        if (binary) {
          builder.field("filter", aliasMetaData.filter.compressed());
        } else {
          byte[] data = aliasMetaData.filter().uncompressed();
          XContentParser parser = XContentFactory.xContent(data).createParser(data);
          Map<String, Object> filter = parser.mapOrdered();
          parser.close();
          builder.field("filter", filter);
        }
      }
      if (aliasMetaData.indexRouting() != null) {
        builder.field("index_routing", aliasMetaData.indexRouting());
      }
      if (aliasMetaData.searchRouting() != null) {
        builder.field("search_routing", aliasMetaData.searchRouting());
      }

      builder.endObject();
    }
  // 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 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;
    if (result.getIndices().size() > 1) {
      String[] indexNames = new String[result.getIndices().size()];
      int i = 0;
      for (IndexMetaData indexMetaData : result.getIndices()) {
        indexNames[i++] = indexMetaData.getIndex().getName();
      }
      throw new IllegalArgumentException(
          "Alias ["
              + aliasOrIndex
              + "] has more than one index associated with it ["
              + Arrays.toString(indexNames)
              + "], can't execute a single index op");
    }
    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");
        }
      }
      // Alias routing overrides the parent routing (if any).
      return aliasMd.indexRouting();
    }
    if (routing == null) {
      return parent;
    }
    return routing;
  }
  @Test
  public void testIndicesAliasesAcknowledgement() {
    createIndex("test");

    // testing acknowledgement when trying to submit an existing alias too
    // in that case it would not make any change, but we are sure about the cluster state
    // as the previous operation was acknowledged
    for (int i = 0; i < 2; i++) {
      assertAcked(client().admin().indices().prepareAliases().addAlias("test", "alias"));

      for (Client client : clients()) {
        AliasMetaData aliasMetaData =
            ((AliasOrIndex.Alias)
                    getLocalClusterState(client).metaData().getAliasAndIndexLookup().get("alias"))
                .getFirstAliasMetaData();
        assertThat(aliasMetaData.alias(), equalTo("alias"));
      }
    }
  }
 private Query parse(AliasMetaData alias, QueryShardContext shardContext) {
   if (alias.filter() == null) {
     return null;
   }
   try {
     byte[] filterSource = alias.filter().uncompressed();
     try (XContentParser parser =
         XContentFactory.xContent(filterSource).createParser(filterSource)) {
       Optional<QueryBuilder> innerQueryBuilder =
           shardContext.newParseContext(parser).parseInnerQueryBuilder();
       if (innerQueryBuilder.isPresent()) {
         return shardContext.toFilter(innerQueryBuilder.get()).query();
       }
       return null;
     }
   } catch (IOException ex) {
     throw new AliasFilterParsingException(
         shardContext.index(), alias.getAlias(), "Invalid alias filter", ex);
   }
 }
  /**
   * Finds the specific index aliases that match with the specified aliases directly or partially
   * via wildcards and that point to the specified concrete indices or match partially with the
   * indices via wildcards.
   *
   * @param aliases The names of the index aliases to find
   * @param concreteIndices The concrete indexes the index aliases must point to order to be
   *     returned.
   * @return the found index aliases grouped by index
   */
  public ImmutableOpenMap<String, List<AliasMetaData>> findAliases(
      final String[] aliases, String[] concreteIndices) {
    assert aliases != null;
    assert concreteIndices != null;
    if (concreteIndices.length == 0) {
      return ImmutableOpenMap.of();
    }

    boolean matchAllAliases = matchAllAliases(aliases);
    ImmutableOpenMap.Builder<String, List<AliasMetaData>> mapBuilder = ImmutableOpenMap.builder();
    Iterable<String> intersection =
        HppcMaps.intersection(ObjectHashSet.from(concreteIndices), indices.keys());
    for (String index : intersection) {
      IndexMetaData indexMetaData = indices.get(index);
      List<AliasMetaData> filteredValues = new ArrayList<>();
      for (ObjectCursor<AliasMetaData> cursor : indexMetaData.getAliases().values()) {
        AliasMetaData value = cursor.value;
        if (matchAllAliases || Regex.simpleMatch(aliases, value.alias())) {
          filteredValues.add(value);
        }
      }

      if (!filteredValues.isEmpty()) {
        // Make the list order deterministic
        CollectionUtil.timSort(
            filteredValues,
            new Comparator<AliasMetaData>() {
              @Override
              public int compare(AliasMetaData o1, AliasMetaData o2) {
                return o1.alias().compareTo(o2.alias());
              }
            });
        mapBuilder.put(index, Collections.unmodifiableList(filteredValues));
      }
    }
    return mapBuilder.build();
  }
  /**
   * Checks if at least one of the specified aliases exists in the specified concrete indices.
   * Wildcards are supported in the alias names for partial matches.
   *
   * @param aliases The names of the index aliases to find
   * @param concreteIndices The concrete indexes the index aliases must point to order to be
   *     returned.
   * @return whether at least one of the specified aliases exists in one of the specified concrete
   *     indices.
   */
  public boolean hasAliases(final String[] aliases, String[] concreteIndices) {
    assert aliases != null;
    assert concreteIndices != null;
    if (concreteIndices.length == 0) {
      return false;
    }

    Iterable<String> intersection =
        HppcMaps.intersection(ObjectHashSet.from(concreteIndices), indices.keys());
    for (String index : intersection) {
      IndexMetaData indexMetaData = indices.get(index);
      List<AliasMetaData> filteredValues = new ArrayList<>();
      for (ObjectCursor<AliasMetaData> cursor : indexMetaData.getAliases().values()) {
        AliasMetaData value = cursor.value;
        if (Regex.simpleMatch(aliases, value.alias())) {
          filteredValues.add(value);
        }
      }
      if (!filteredValues.isEmpty()) {
        return true;
      }
    }
    return false;
  }
 public static void writeTo(AliasMetaData aliasMetaData, StreamOutput out) throws IOException {
   out.writeString(aliasMetaData.alias());
   if (aliasMetaData.filter() != null) {
     out.writeBoolean(true);
     aliasMetaData.filter.writeTo(out);
   } else {
     out.writeBoolean(false);
   }
   if (aliasMetaData.indexRouting() != null) {
     out.writeBoolean(true);
     out.writeString(aliasMetaData.indexRouting());
   } else {
     out.writeBoolean(false);
   }
   if (aliasMetaData.searchRouting() != null) {
     out.writeBoolean(true);
     out.writeString(aliasMetaData.searchRouting());
   } else {
     out.writeBoolean(false);
   }
 }
 public Builder putAlias(AliasMetaData aliasMetaData) {
   aliases.put(aliasMetaData.alias(), aliasMetaData);
   return this;
 }
 private AliasMetaData(AliasMetaData aliasMetaData, String alias) {
   this(
       alias, aliasMetaData.filter(), aliasMetaData.indexRouting(), aliasMetaData.searchRouting());
 }
 public Builder(AliasMetaData aliasMetaData) {
   this(aliasMetaData.alias());
   filter = aliasMetaData.filter();
   indexRouting = aliasMetaData.indexRouting();
   searchRouting = aliasMetaData.searchRouting();
 }
    public MetaData build() {
      // TODO: We should move these datastructures to IndexNameExpressionResolver, this will give
      // the following benefits:
      // 1) The datastructures will only be rebuilded when needed. Now during serializing we rebuild
      // these datastructures
      //    while these datastructures aren't even used.
      // 2) The aliasAndIndexLookup can be updated instead of rebuilding it all the time.

      // build all concrete indices arrays:
      // TODO: I think we can remove these arrays. it isn't worth the effort, for operations on all
      // indices.
      // When doing an operation across all indices, most of the time is spent on actually going to
      // all shards and
      // do the required operations, the bottleneck isn't resolving expressions into concrete
      // indices.
      List<String> allIndicesLst = new ArrayList<>();
      for (ObjectCursor<IndexMetaData> cursor : indices.values()) {
        allIndicesLst.add(cursor.value.getIndex().getName());
      }
      String[] allIndices = allIndicesLst.toArray(new String[allIndicesLst.size()]);

      List<String> allOpenIndicesLst = new ArrayList<>();
      List<String> allClosedIndicesLst = new ArrayList<>();
      for (ObjectCursor<IndexMetaData> cursor : indices.values()) {
        IndexMetaData indexMetaData = cursor.value;
        if (indexMetaData.getState() == IndexMetaData.State.OPEN) {
          allOpenIndicesLst.add(indexMetaData.getIndex().getName());
        } else if (indexMetaData.getState() == IndexMetaData.State.CLOSE) {
          allClosedIndicesLst.add(indexMetaData.getIndex().getName());
        }
      }
      String[] allOpenIndices = allOpenIndicesLst.toArray(new String[allOpenIndicesLst.size()]);
      String[] allClosedIndices =
          allClosedIndicesLst.toArray(new String[allClosedIndicesLst.size()]);

      // build all indices map
      SortedMap<String, AliasOrIndex> aliasAndIndexLookup = new TreeMap<>();
      for (ObjectCursor<IndexMetaData> cursor : indices.values()) {
        IndexMetaData indexMetaData = cursor.value;
        aliasAndIndexLookup.put(
            indexMetaData.getIndex().getName(), new AliasOrIndex.Index(indexMetaData));

        for (ObjectObjectCursor<String, AliasMetaData> aliasCursor : indexMetaData.getAliases()) {
          AliasMetaData aliasMetaData = aliasCursor.value;
          AliasOrIndex aliasOrIndex = aliasAndIndexLookup.get(aliasMetaData.getAlias());
          if (aliasOrIndex == null) {
            aliasOrIndex = new AliasOrIndex.Alias(aliasMetaData, indexMetaData);
            aliasAndIndexLookup.put(aliasMetaData.getAlias(), aliasOrIndex);
          } else if (aliasOrIndex instanceof AliasOrIndex.Alias) {
            AliasOrIndex.Alias alias = (AliasOrIndex.Alias) aliasOrIndex;
            alias.addIndex(indexMetaData);
          } else if (aliasOrIndex instanceof AliasOrIndex.Index) {
            AliasOrIndex.Index index = (AliasOrIndex.Index) aliasOrIndex;
            throw new IllegalStateException(
                "index and alias names need to be unique, but alias ["
                    + aliasMetaData.getAlias()
                    + "] and index "
                    + index.getIndex().getIndex()
                    + " have the same name");
          } else {
            throw new IllegalStateException(
                "unexpected alias [" + aliasMetaData.getAlias() + "][" + aliasOrIndex + "]");
          }
        }
      }
      aliasAndIndexLookup = Collections.unmodifiableSortedMap(aliasAndIndexLookup);
      return new MetaData(
          clusterUUID,
          version,
          transientSettings,
          persistentSettings,
          indices.build(),
          templates.build(),
          customs.build(),
          allIndices,
          allOpenIndices,
          allClosedIndices,
          aliasAndIndexLookup);
    }