4848import java .util .Collections ;
4949import java .util .HashMap ;
5050import java .util .HashSet ;
51- import java .util .Iterator ;
5251import java .util .List ;
5352import java .util .Map ;
5453import java .util .Objects ;
@@ -253,7 +252,7 @@ protected static Collection<String> resolveExpressions(Context context, String..
253252 } else {
254253 return ExplicitResourceNameFilter .filterUnavailable (
255254 context ,
256- DateMathExpressionResolver .resolve (context , List . of (expressions ))
255+ DateMathExpressionResolver .resolve (context , Arrays . asList (expressions ))
257256 );
258257 }
259258 } else {
@@ -264,7 +263,10 @@ protected static Collection<String> resolveExpressions(Context context, String..
264263 } else {
265264 return WildcardExpressionResolver .resolve (
266265 context ,
267- ExplicitResourceNameFilter .filterUnavailable (context , DateMathExpressionResolver .resolve (context , List .of (expressions )))
266+ ExplicitResourceNameFilter .filterUnavailable (
267+ context ,
268+ DateMathExpressionResolver .resolve (context , Arrays .asList (expressions ))
269+ )
268270 );
269271 }
270272 }
@@ -1294,34 +1296,51 @@ private static boolean shouldIncludeIfAlias(IndexAbstraction ia, IndexNameExpres
12941296 * </ol>
12951297 */
12961298 public static Collection <String > resolve (Context context , List <String > expressions ) {
1297- ExpressionList expressionList = new ExpressionList (context , expressions );
12981299 // fast exit if there are no wildcards to evaluate
1299- if (expressionList .hasWildcard () == false ) {
1300+ if (context .getOptions ().expandWildcardExpressions () == false ) {
1301+ return expressions ;
1302+ }
1303+ int firstWildcardIndex = 0 ;
1304+ for (; firstWildcardIndex < expressions .size (); firstWildcardIndex ++) {
1305+ String expression = expressions .get (firstWildcardIndex );
1306+ if (isWildcard (expression )) {
1307+ break ;
1308+ }
1309+ }
1310+ if (firstWildcardIndex == expressions .size ()) {
13001311 return expressions ;
13011312 }
13021313 Set <String > result = new HashSet <>();
1303- for (ExpressionList .Expression expression : expressionList ) {
1304- if (expression .isWildcard ()) {
1305- Stream <IndexAbstraction > matchingResources = matchResourcesToWildcard (context , expression .get ());
1314+ for (int i = 0 ; i < firstWildcardIndex ; i ++) {
1315+ result .add (expressions .get (i ));
1316+ }
1317+ AtomicBoolean emptyWildcardExpansion = context .getOptions ().allowNoIndices () ? null : new AtomicBoolean ();
1318+ for (int i = firstWildcardIndex ; i < expressions .size (); i ++) {
1319+ String expression = expressions .get (i );
1320+ boolean isExclusion = i > firstWildcardIndex && expression .charAt (0 ) == '-' ;
1321+ if (i == firstWildcardIndex || isWildcard (expression )) {
1322+ Stream <IndexAbstraction > matchingResources = matchResourcesToWildcard (
1323+ context ,
1324+ isExclusion ? expression .substring (1 ) : expression
1325+ );
13061326 Stream <String > matchingOpenClosedNames = expandToOpenClosed (context , matchingResources );
1307- AtomicBoolean emptyWildcardExpansion = new AtomicBoolean (false );
1308- if (context .getOptions ().allowNoIndices () == false ) {
1327+ if (emptyWildcardExpansion != null ) {
13091328 emptyWildcardExpansion .set (true );
13101329 matchingOpenClosedNames = matchingOpenClosedNames .peek (x -> emptyWildcardExpansion .set (false ));
13111330 }
1312- if (expression . isExclusion () ) {
1313- matchingOpenClosedNames .forEachOrdered (result ::remove );
1331+ if (isExclusion ) {
1332+ matchingOpenClosedNames .forEach (result ::remove );
13141333 } else {
1315- matchingOpenClosedNames .forEachOrdered (result ::add );
1334+ matchingOpenClosedNames .forEach (result ::add );
13161335 }
1317- if (emptyWildcardExpansion .get ()) {
1318- throw notFoundException (expression . get () );
1336+ if (emptyWildcardExpansion != null && emptyWildcardExpansion .get ()) {
1337+ throw notFoundException (expression );
13191338 }
13201339 } else {
1321- if (expression . isExclusion () ) {
1322- result .remove (expression .get ( ));
1340+ if (isExclusion ) {
1341+ result .remove (expression .substring ( 1 ));
13231342 } else {
1324- result .add (expression . get () );
1343+ result .add (expression );
13251344 }
13261345 }
13271346 }
@@ -1507,27 +1526,35 @@ private DateMathExpressionResolver() {
15071526 // utility class
15081527 }
15091528
1529+ /**
1530+ * Resolves date math expressions. If this is a noop the given {@code expressions} list is returned without copying.
1531+ * As a result callers of this method should not mutate the returned list. Mutating it may come with unexpected side effects.
1532+ */
15101533 public static List <String > resolve (Context context , List <String > expressions ) {
1511- List <String > result = new ArrayList <>(expressions .size ());
1512- for (ExpressionList .Expression expression : new ExpressionList (context , expressions )) {
1513- result .add (resolveExpression (expression , context ::getStartTime ));
1534+ boolean wildcardSeen = false ;
1535+ final boolean expandWildcards = context .getOptions ().expandWildcardExpressions ();
1536+ String [] result = null ;
1537+ for (int i = 0 , n = expressions .size (); i < n ; i ++) {
1538+ String expression = expressions .get (i );
1539+ // accepts date-math exclusions that are of the form "-<...{}>",f i.e. the "-" is outside the "<>" date-math template
1540+ boolean isExclusion = wildcardSeen && expression .startsWith ("-" );
1541+ wildcardSeen = wildcardSeen || (expandWildcards && isWildcard (expression ));
1542+ String toResolve = isExclusion ? expression .substring (1 ) : expression ;
1543+ String resolved = resolveExpression (toResolve , context ::getStartTime );
1544+ if (toResolve != resolved ) {
1545+ if (result == null ) {
1546+ result = expressions .toArray (Strings .EMPTY_ARRAY );
1547+ }
1548+ result [i ] = isExclusion ? "-" + resolved : resolved ;
1549+ }
15141550 }
1515- return result ;
1551+ return result == null ? expressions : Arrays . asList ( result ) ;
15161552 }
15171553
15181554 static String resolveExpression (String expression ) {
15191555 return resolveExpression (expression , System ::currentTimeMillis );
15201556 }
15211557
1522- static String resolveExpression (ExpressionList .Expression expression , LongSupplier getTime ) {
1523- if (expression .isExclusion ()) {
1524- // accepts date-math exclusions that are of the form "-<...{}>", i.e. the "-" is outside the "<>" date-math template
1525- return "-" + resolveExpression (expression .get (), getTime );
1526- } else {
1527- return resolveExpression (expression .get (), getTime );
1528- }
1529- }
1530-
15311558 static String resolveExpression (String expression , LongSupplier getTime ) {
15321559 if (expression .startsWith (EXPRESSION_LEFT_BOUND ) == false || expression .endsWith (EXPRESSION_RIGHT_BOUND ) == false ) {
15331560 return expression ;
@@ -1689,14 +1716,35 @@ private ExplicitResourceNameFilter() {
16891716 */
16901717 public static List <String > filterUnavailable (Context context , List <String > expressions ) {
16911718 ensureRemoteIndicesRequireIgnoreUnavailable (context .getOptions (), expressions );
1692- List <String > result = new ArrayList <>(expressions .size ());
1693- for (ExpressionList .Expression expression : new ExpressionList (context , expressions )) {
1694- validateAliasOrIndex (expression );
1695- if (expression .isWildcard () || expression .isExclusion () || ensureAliasOrIndexExists (context , expression .get ())) {
1696- result .add (expression .expression ());
1719+ final boolean expandWildcards = context .getOptions ().expandWildcardExpressions ();
1720+ boolean wildcardSeen = false ;
1721+ List <String > result = null ;
1722+ for (int i = 0 ; i < expressions .size (); i ++) {
1723+ String expression = expressions .get (i );
1724+ if (Strings .isEmpty (expression )) {
1725+ throw notFoundException (expression );
1726+ }
1727+ // Expressions can not start with an underscore. This is reserved for APIs. If the check gets here, the API
1728+ // does not exist and the path is interpreted as an expression. If the expression begins with an underscore,
1729+ // throw a specific error that is different from the [[IndexNotFoundException]], which is typically thrown
1730+ // if the expression can't be found.
1731+ if (expression .charAt (0 ) == '_' ) {
1732+ throw new InvalidIndexNameException (expression , "must not start with '_'." );
1733+ }
1734+ final boolean isWildcard = expandWildcards && isWildcard (expression );
1735+ if (isWildcard || (wildcardSeen && expression .charAt (0 ) == '-' ) || ensureAliasOrIndexExists (context , expression )) {
1736+ if (result != null ) {
1737+ result .add (expression );
1738+ }
1739+ } else {
1740+ if (result == null ) {
1741+ result = new ArrayList <>(expressions .size () - 1 );
1742+ result .addAll (expressions .subList (0 , i ));
1743+ }
16971744 }
1745+ wildcardSeen |= isWildcard ;
16981746 }
1699- return result ;
1747+ return result == null ? expressions : result ;
17001748 }
17011749
17021750 /**
@@ -1736,19 +1784,6 @@ private static boolean ensureAliasOrIndexExists(Context context, String name) {
17361784 return true ;
17371785 }
17381786
1739- private static void validateAliasOrIndex (ExpressionList .Expression expression ) {
1740- if (Strings .isEmpty (expression .expression ())) {
1741- throw notFoundException (expression .expression ());
1742- }
1743- // Expressions can not start with an underscore. This is reserved for APIs. If the check gets here, the API
1744- // does not exist and the path is interpreted as an expression. If the expression begins with an underscore,
1745- // throw a specific error that is different from the [[IndexNotFoundException]], which is typically thrown
1746- // if the expression can't be found.
1747- if (expression .expression ().charAt (0 ) == '_' ) {
1748- throw new InvalidIndexNameException (expression .expression (), "must not start with '_'." );
1749- }
1750- }
1751-
17521787 private static void ensureRemoteIndicesRequireIgnoreUnavailable (IndicesOptions options , List <String > indexExpressions ) {
17531788 if (options .ignoreUnavailable ()) {
17541789 return ;
@@ -1773,57 +1808,6 @@ private static void failOnRemoteIndicesNotIgnoringUnavailable(List<String> index
17731808 }
17741809 }
17751810
1776- /**
1777- * Used to iterate expression lists and work out which expression item is a wildcard or an exclusion.
1778- */
1779- public static final class ExpressionList implements Iterable <ExpressionList .Expression > {
1780- private final List <Expression > expressionsList ;
1781- private final boolean hasWildcard ;
1782-
1783- public record Expression (String expression , boolean isWildcard , boolean isExclusion ) {
1784- public String get () {
1785- if (isExclusion ()) {
1786- // drop the leading "-" if exclusion because it is easier for callers to handle it like this
1787- return expression ().substring (1 );
1788- } else {
1789- return expression ();
1790- }
1791- }
1792- }
1793-
1794- /**
1795- * Creates the expression iterable that can be used to easily check which expression item is a wildcard or an exclusion (or both).
1796- * The {@param context} is used to check if wildcards ought to be considered or not.
1797- */
1798- public ExpressionList (Context context , List <String > expressionStrings ) {
1799- List <Expression > expressionsList = new ArrayList <>(expressionStrings .size ());
1800- boolean wildcardSeen = false ;
1801- for (String expressionString : expressionStrings ) {
1802- boolean isExclusion = expressionString .startsWith ("-" ) && wildcardSeen ;
1803- if (context .getOptions ().expandWildcardExpressions () && isWildcard (expressionString )) {
1804- wildcardSeen = true ;
1805- expressionsList .add (new Expression (expressionString , true , isExclusion ));
1806- } else {
1807- expressionsList .add (new Expression (expressionString , false , isExclusion ));
1808- }
1809- }
1810- this .expressionsList = expressionsList ;
1811- this .hasWildcard = wildcardSeen ;
1812- }
1813-
1814- /**
1815- * Returns {@code true} if the expression contains any wildcard and the options allow wildcard expansion
1816- */
1817- public boolean hasWildcard () {
1818- return this .hasWildcard ;
1819- }
1820-
1821- @ Override
1822- public Iterator <ExpressionList .Expression > iterator () {
1823- return expressionsList .iterator ();
1824- }
1825- }
1826-
18271811 /**
18281812 * This is a context for the DateMathExpressionResolver which does not require {@code IndicesOptions} or {@code ClusterState}
18291813 * since it uses only the start time to resolve expressions.
0 commit comments