From 4af9812336d2ca0184812a4eb7880048c28ce23d Mon Sep 17 00:00:00 2001 From: Shawn Qiang <814238703@qq.com> Date: Thu, 12 Mar 2026 21:02:52 +0800 Subject: [PATCH] Fix array_index_out_of_bounds_exception with wildcard and aggregations Signed-off-by: Shawn Qiang <814238703@qq.com> --- CHANGELOG.md | 3 ++- .../index/mapper/WildcardFieldMapper.java | 15 +++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83756fce81c6b..8e0190c725670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,8 +67,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Fix ShardSearchFailure in transport-grpc ([#20641](https://github.com/opensearch-project/OpenSearch/pull/20641)) - Fix TLS cert hot-reload for Arrow Flight transport ([#20732](https://github.com/opensearch-project/OpenSearch/pull/20732)) - Fix misleading heap usage cancellation message in SearchBackpressureService ([#20779](https://github.com/opensearch-project/OpenSearch/pull/20779)) -- - Delegate getMin/getMax methods for ExitableTerms ([#20775](https://github.com/opensearch-project/OpenSearch/pull/20775)) +- Delegate getMin/getMax methods for ExitableTerms ([#20775](https://github.com/opensearch-project/OpenSearch/pull/20775)) - Fix terms lookup subquery fetch limit reading from non-existent index setting instead of cluster `max_clause_count` ([#20823](https://github.com/opensearch-project/OpenSearch/pull/20823)) +- Fix array_index_out_of_bounds_exception with wildcard and aggregations ([#20842](https://github.com/opensearch-project/OpenSearch/pull/20842)) ### Dependencies - Bump shadow-gradle-plugin from 8.3.9 to 9.3.1 ([#20569](https://github.com/opensearch-project/OpenSearch/pull/20569)) 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 c93673dee5cb2..2fd08ccfbd823 100644 --- a/server/src/main/java/org/opensearch/index/mapper/WildcardFieldMapper.java +++ b/server/src/main/java/org/opensearch/index/mapper/WildcardFieldMapper.java @@ -743,7 +743,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) { @@ -764,10 +764,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; } } @@ -776,14 +776,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; } @@ -821,7 +821,7 @@ public Query rewrite(IndexSearcher indexSearcher) throws IOException { rewriteFirstPhase, secondPhaseMatcher, patternString, - valueFetcher, + valueFetcherSupplier, searchLookup ); } @@ -844,6 +844,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) {