6262import java .time .Instant ;
6363import java .util .ArrayList ;
6464import java .util .Arrays ;
65+ import java .util .Collection ;
6566import java .util .Collections ;
6667import java .util .Comparator ;
6768import java .util .HashMap ;
@@ -1087,9 +1088,8 @@ static Set<String> dataStreamsExclusivelyUsingTemplates(final ProjectMetadata pr
10871088 .map (templateName -> projectMetadata .templatesV2 ().get (templateName ))
10881089 .filter (Objects ::nonNull )
10891090 .map (ComposableIndexTemplate ::indexPatterns )
1090- .map (Set ::copyOf )
1091- .reduce (Sets ::union )
1092- .orElse (Set .of ());
1091+ .flatMap (List ::stream )
1092+ .collect (Collectors .toSet ());
10931093
10941094 return projectMetadata .dataStreams ()
10951095 .values ()
@@ -1099,9 +1099,10 @@ static Set<String> dataStreamsExclusivelyUsingTemplates(final ProjectMetadata pr
10991099 .filter (ds -> {
11001100 // Retrieve the templates that match the data stream name ordered by priority
11011101 List <Tuple <String , ComposableIndexTemplate >> candidates = findV2CandidateTemplates (
1102- projectMetadata ,
1102+ projectMetadata . templatesV2 (). entrySet () ,
11031103 ds .getName (),
1104- ds .isHidden ()
1104+ ds .isHidden (),
1105+ false
11051106 );
11061107 if (candidates .isEmpty ()) {
11071108 throw new IllegalStateException ("Data stream " + ds .getName () + " did not match any composable index templates." );
@@ -1110,13 +1111,10 @@ static Set<String> dataStreamsExclusivelyUsingTemplates(final ProjectMetadata pr
11101111 // Limit data streams that can ONLY use any of the specified templates, we do this by filtering
11111112 // the matching templates that are others than the ones requested and could be a valid template to use.
11121113 return candidates .stream ()
1113- .filter (
1114+ .noneMatch (
11141115 template -> templateNames .contains (template .v1 ()) == false
11151116 && isGlobalAndHasIndexHiddenSetting (projectMetadata , template .v2 (), template .v1 ()) == false
1116- )
1117- .map (Tuple ::v1 )
1118- .toList ()
1119- .isEmpty ();
1117+ );
11201118 })
11211119 .map (DataStream ::getName )
11221120 .collect (Collectors .toSet ());
@@ -1357,7 +1355,41 @@ public static List<IndexTemplateMetadata> findV1Templates(
13571355 */
13581356 @ Nullable
13591357 public static String findV2Template (ProjectMetadata projectMetadata , String indexName , boolean isHidden ) {
1360- final List <Tuple <String , ComposableIndexTemplate >> candidates = findV2CandidateTemplates (projectMetadata , indexName , isHidden );
1358+ return findV2Template (projectMetadata , projectMetadata .templatesV2 ().entrySet (), indexName , isHidden , false );
1359+ }
1360+
1361+ /**
1362+ * Return the name (id) of the highest matching index template out of the provided templates (that <i>need</i> to be sorted descending
1363+ * on priority beforehand), or the given index name. In the event that no templates are matched, {@code null} is returned.
1364+ */
1365+ @ Nullable
1366+ public static String findV2TemplateFromSortedList (
1367+ ProjectMetadata projectMetadata ,
1368+ Collection <Map .Entry <String , ComposableIndexTemplate >> templates ,
1369+ String indexName ,
1370+ boolean isHidden
1371+ ) {
1372+ return findV2Template (projectMetadata , templates , indexName , isHidden , true );
1373+ }
1374+
1375+ /**
1376+ * Return the name (id) of the highest matching index template, out of the provided templates, for the given index name. In
1377+ * the event that no templates are matched, {@code null} is returned.
1378+ */
1379+ @ Nullable
1380+ private static String findV2Template (
1381+ ProjectMetadata projectMetadata ,
1382+ Collection <Map .Entry <String , ComposableIndexTemplate >> templates ,
1383+ String indexName ,
1384+ boolean isHidden ,
1385+ boolean exitOnFirstMatch
1386+ ) {
1387+ final List <Tuple <String , ComposableIndexTemplate >> candidates = findV2CandidateTemplates (
1388+ templates ,
1389+ indexName ,
1390+ isHidden ,
1391+ exitOnFirstMatch
1392+ );
13611393 if (candidates .isEmpty ()) {
13621394 return null ;
13631395 }
@@ -1386,33 +1418,39 @@ public static String findV2Template(ProjectMetadata projectMetadata, String inde
13861418 * Return an ordered list of the name (id) and composable index templates that would apply to an index. The first
13871419 * one is the winner template that is applied to this index. In the event that no templates are matched,
13881420 * an empty list is returned.
1421+ * @param templates a list of template entries (name, template) - needs to be sorted when {@code exitOnFirstMatch} is {@code true}
1422+ * @param indexName the index (or data stream) name that should be used for matching the index patterns on the templates
1423+ * @param isHidden whether {@code indexName} belongs to a hidden index - this option is redundant for data streams, as backing indices
1424+ * of data streams will always be returned, regardless of whether the data stream is hidden or not
1425+ * @param exitOnFirstMatch if true, we return immediately after finding a match. That means that the <code>templates</code>
1426+ * parameter needs to be sorted based on priority (descending) for this method to return a sensible result,
1427+ * otherwise this method would just return the first template that matches the name, in an unspecified order
13891428 */
1390- static List <Tuple <String , ComposableIndexTemplate >> findV2CandidateTemplates (
1391- ProjectMetadata projectMetadata ,
1429+ private static List <Tuple <String , ComposableIndexTemplate >> findV2CandidateTemplates (
1430+ Collection < Map . Entry < String , ComposableIndexTemplate >> templates ,
13921431 String indexName ,
1393- boolean isHidden
1432+ boolean isHidden ,
1433+ boolean exitOnFirstMatch
13941434 ) {
1435+ assert exitOnFirstMatch == false || areTemplatesSorted (templates ) : "Expected templates to be sorted" ;
13951436 final String resolvedIndexName = IndexNameExpressionResolver .DateMathExpressionResolver .resolveExpression (indexName );
13961437 final Predicate <String > patternMatchPredicate = pattern -> Regex .simpleMatch (pattern , resolvedIndexName );
13971438 final List <Tuple <String , ComposableIndexTemplate >> candidates = new ArrayList <>();
1398- for (Map .Entry <String , ComposableIndexTemplate > entry : projectMetadata . templatesV2 (). entrySet () ) {
1439+ for (Map .Entry <String , ComposableIndexTemplate > entry : templates ) {
13991440 final String name = entry .getKey ();
14001441 final ComposableIndexTemplate template = entry .getValue ();
14011442 /*
14021443 * We do not ordinarily return match-all templates for hidden indices. But all backing indices for data streams are hidden,
14031444 * and we do want to return even match-all templates for those. Not doing so can result in a situation where a data stream is
14041445 * built with a template that none of its indices match.
14051446 */
1406- if (isHidden == false || template .getDataStreamTemplate () != null ) {
1407- if (anyMatch (template .indexPatterns (), patternMatchPredicate )) {
1408- candidates .add (Tuple .tuple (name , template ));
1409- }
1410- } else {
1411- final boolean isNotMatchAllTemplate = noneMatch (template .indexPatterns (), Regex ::isMatchAllPattern );
1412- if (isNotMatchAllTemplate ) {
1413- if (anyMatch (template .indexPatterns (), patternMatchPredicate )) {
1414- candidates .add (Tuple .tuple (name , template ));
1415- }
1447+ if (anyMatch (template .indexPatterns (), Regex ::isMatchAllPattern ) && isHidden && template .getDataStreamTemplate () == null ) {
1448+ continue ;
1449+ }
1450+ if (anyMatch (template .indexPatterns (), patternMatchPredicate )) {
1451+ candidates .add (Tuple .tuple (name , template ));
1452+ if (exitOnFirstMatch ) {
1453+ return candidates ;
14161454 }
14171455 }
14181456 }
@@ -1421,6 +1459,17 @@ static List<Tuple<String, ComposableIndexTemplate>> findV2CandidateTemplates(
14211459 return candidates ;
14221460 }
14231461
1462+ private static boolean areTemplatesSorted (Collection <Map .Entry <String , ComposableIndexTemplate >> templates ) {
1463+ ComposableIndexTemplate previousTemplate = null ;
1464+ for (Map .Entry <String , ComposableIndexTemplate > template : templates ) {
1465+ if (previousTemplate != null && template .getValue ().priorityOrZero () > previousTemplate .priorityOrZero ()) {
1466+ return false ;
1467+ }
1468+ previousTemplate = template .getValue ();
1469+ }
1470+ return true ;
1471+ }
1472+
14241473 // Checks if a global template specifies the `index.hidden` setting. This check is important because a global
14251474 // template shouldn't specify the `index.hidden` setting, we leave it up to the caller to handle this situation.
14261475 private static boolean isGlobalAndHasIndexHiddenSetting (
0 commit comments