diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad5fd179384a..44f26d14992a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed - Harden the circuit breaker and failure handle logic in query result consumer ([#19396](https://github.com/opensearch-project/OpenSearch/pull/19396)) - Fix case insensitive and escaped query on wildcard ([#16827](https://github.com/opensearch-project/OpenSearch/pull/16827)) +- Fix array_index_out_of_bounds_exception with wildcard and aggregations ([#20842](https://github.com/opensearch-project/OpenSearch/pull/20842)) ### Security diff --git a/server/src/main/java/org/opensearch/index/mapper/WildcardFieldMapper.java b/server/src/main/java/org/opensearch/index/mapper/WildcardFieldMapper.java index 721b047573a60..833b9b327ab76 100644 --- a/server/src/main/java/org/opensearch/index/mapper/WildcardFieldMapper.java +++ b/server/src/main/java/org/opensearch/index/mapper/WildcardFieldMapper.java @@ -773,7 +773,7 @@ static class WildcardMatchingQuery extends Query { private final Query firstPhaseQuery; private final Predicate secondPhaseMatcher; private final String patternString; // For toString - private final ValueFetcher valueFetcher; + private final Supplier valueFetcherSupplier; private final SearchLookup searchLookup; WildcardMatchingQuery(String fieldName, Query firstPhaseQuery, String patternString) { @@ -794,10 +794,10 @@ public WildcardMatchingQuery( this.patternString = Objects.requireNonNull(patternString); if (context != null) { this.searchLookup = context.lookup(); - this.valueFetcher = fieldType.valueFetcher(context, context.lookup(), null); + this.valueFetcherSupplier = () -> fieldType.valueFetcher(context, context.lookup(), null); } else { this.searchLookup = null; - this.valueFetcher = null; + this.valueFetcherSupplier = null; } } @@ -806,14 +806,14 @@ private WildcardMatchingQuery( Query firstPhaseQuery, Predicate secondPhaseMatcher, String patternString, - ValueFetcher valueFetcher, + Supplier valueFetcherSupplier, SearchLookup searchLookup ) { this.fieldName = fieldName; this.firstPhaseQuery = firstPhaseQuery; this.secondPhaseMatcher = secondPhaseMatcher; this.patternString = patternString; - this.valueFetcher = valueFetcher; + this.valueFetcherSupplier = valueFetcherSupplier; this.searchLookup = searchLookup; } @@ -851,7 +851,7 @@ public Query rewrite(IndexSearcher indexSearcher) throws IOException { rewriteFirstPhase, secondPhaseMatcher, patternString, - valueFetcher, + valueFetcherSupplier, searchLookup ); } @@ -884,6 +884,9 @@ public Scorer get(long leadCost) throws IOException { Scorer approximateScorer = firstPhaseSupplier.get(leadCost); DocIdSetIterator approximation = approximateScorer.iterator(); LeafSearchLookup leafSearchLookup = searchLookup.getLeafSearchLookup(context); + // Create a new ValueFetcher per thread. + // ValueFetcher.setNextReader is not thread safe. + final ValueFetcher valueFetcher = valueFetcherSupplier.get(); valueFetcher.setNextReader(context); TwoPhaseIterator twoPhaseIterator = new TwoPhaseIterator(approximation) {