diff --git a/CHANGELOG-3.0.md b/CHANGELOG-3.0.md index 14874ceb7c393..8b00435a7cdf1 100644 --- a/CHANGELOG-3.0.md +++ b/CHANGELOG-3.0.md @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Add task completion count in search backpressure stats API ([#10028](https://github.com/opensearch-project/OpenSearch/pull/10028/)) - Deprecate CamelCase `PathHierarchy` tokenizer name in favor to lowercase `path_hierarchy` ([#10894](https://github.com/opensearch-project/OpenSearch/pull/10894)) - Breaking change: Do not request "search_pipelines" metrics by default in NodesInfoRequest ([#12497](https://github.com/opensearch-project/OpenSearch/pull/12497)) +- Use simpler matching logic for source fields when explicit field names (no wildcards or dot-paths) are specified ([#17160](https://github.com/opensearch-project/OpenSearch/pull/17160)) - Refactor `:libs` module `bootstrap` package to eliminate top level split packages for JPMS support ([#17117](https://github.com/opensearch-project/OpenSearch/pull/17117)) - Refactor the codebase to eliminate top level split packages for JPMS support ([#17153](https://github.com/opensearch-project/OpenSearch/pull/17153) - Refactor `:server` module `org.apacge.lucene` package to eliminate top level split packages for JPMS support ([#17241](https://github.com/opensearch-project/OpenSearch/pull/17241)) diff --git a/server/src/main/java/org/opensearch/common/xcontent/support/XContentMapValues.java b/server/src/main/java/org/opensearch/common/xcontent/support/XContentMapValues.java index d3fa44c5afb66..7240252b51d83 100644 --- a/server/src/main/java/org/opensearch/common/xcontent/support/XContentMapValues.java +++ b/server/src/main/java/org/opensearch/common/xcontent/support/XContentMapValues.java @@ -45,9 +45,12 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; /** @@ -216,6 +219,54 @@ public static Map filter(Map map, String[] includes, * @see #filter(Map, String[], String[]) for details */ public static Function, Map> filter(String[] includes, String[] excludes) { + if (hasNoWildcardsOrDots(includes) && hasNoWildcardsOrDots(excludes)) { + return createSetBasedFilter(includes, excludes); + } + return createAutomatonFilter(includes, excludes); + } + + private static boolean hasNoWildcardsOrDots(String[] fields) { + if (fields == null || fields.length == 0) { + return true; + } + + for (String field : fields) { + if (field.indexOf('*') != -1 || field.indexOf('.') != -1) { + return false; + } + } + return true; + } + + /** + * Creates a simple HashSet-based filter for exact field name matching + */ + private static Function, Map> createSetBasedFilter(String[] includes, String[] excludes) { + Set includeSet = (includes == null || includes.length == 0) ? null : new HashSet<>(Arrays.asList(includes)); + Set excludeSet = (excludes == null || excludes.length == 0) + ? Collections.emptySet() + : new HashSet<>(Arrays.asList(excludes)); + + return (map) -> { + Map filtered = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + int dotPos = key.indexOf('.'); + if (dotPos > 0) { + key = key.substring(0, dotPos); + } + if ((includeSet == null || includeSet.contains(key)) && !excludeSet.contains(key)) { + filtered.put(entry.getKey(), entry.getValue()); + } + } + return filtered; + }; + } + + /** + * Creates an automaton-based filter for complex pattern matching + */ + public static Function, Map> createAutomatonFilter(String[] includes, String[] excludes) { CharacterRunAutomaton matchAllAutomaton = new CharacterRunAutomaton(Automata.makeAnyString()); CharacterRunAutomaton include;