@@ -92,13 +92,15 @@ public void resolveAsMergedMapping(
9292
9393 // public for testing only
9494 public static IndexResolution mergedMappings (String indexPattern , FieldCapabilitiesResponse fieldCapsResponse ) {
95- var numberOfIndices = fieldCapsResponse .getIndexResponses ().size ();
9695 assert ThreadPool .assertCurrentThreadPool (ThreadPool .Names .SEARCH_COORDINATION ); // too expensive to run this on a transport worker
96+ var numberOfIndices = fieldCapsResponse .getIndexResponses ().size ();
9797 if (fieldCapsResponse .getIndexResponses ().isEmpty ()) {
9898 return IndexResolution .notFound (indexPattern );
9999 }
100100
101- Map <String , List <IndexFieldCapabilities >> fieldsCaps = collectFieldCaps (fieldCapsResponse );
101+ var collectedFieldCaps = collectFieldCaps (fieldCapsResponse );
102+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps = collectedFieldCaps .fieldsCaps ;
103+ Map <String , Integer > indexMappingHashDuplicates = collectedFieldCaps .indexMappingHashDuplicates ;
102104
103105 // Build hierarchical fields - it's easier to do it in sorted order so the object fields come first.
104106 // TODO flattened is simpler - could we get away with that?
@@ -130,7 +132,8 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
130132 }
131133 // TODO we're careful to make isAlias match IndexResolver - but do we use it?
132134
133- List <IndexFieldCapabilities > fcs = fieldsCaps .get (fullName );
135+ var fieldCap = fieldsCaps .get (fullName );
136+ List <IndexFieldCapabilities > fcs = fieldCap .fieldCapabilities ;
134137 EsField field = firstUnsupportedParent == null
135138 ? createField (fieldCapsResponse , name , fullName , fcs , isAlias )
136139 : new UnsupportedEsField (
@@ -140,8 +143,7 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
140143 new HashMap <>()
141144 );
142145 fields .put (name , field );
143-
144- var isPartiallyUnmapped = fcs .size () < numberOfIndices ;
146+ var isPartiallyUnmapped = fcs .size () + indexMappingHashDuplicates .getOrDefault (fieldCap .indexMappingHash , 0 ) < numberOfIndices ;
145147 if (isPartiallyUnmapped ) {
146148 partiallyUnmappedFields .add (fullName );
147149 }
@@ -162,23 +164,46 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
162164 return IndexResolution .valid (index , concreteIndices .keySet (), failures );
163165 }
164166
165- private static Map <String , List <IndexFieldCapabilities >> collectFieldCaps (FieldCapabilitiesResponse fieldCapsResponse ) {
166- Set <String > seenHashes = new HashSet <>();
167- Map <String , List <IndexFieldCapabilities >> fieldsCaps = new HashMap <>();
167+ private record IndexFieldCapabilitiesWithSourceHash (List <IndexFieldCapabilities > fieldCapabilities , String indexMappingHash ) {}
168+
169+ private record CollectedFieldCaps (
170+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps ,
171+ // The map won't contain entries without duplicates, i.e., it's number of occurrences - 1.
172+ Map <String , Integer > indexMappingHashDuplicates
173+ ) {}
174+
175+ private static CollectedFieldCaps collectFieldCaps (FieldCapabilitiesResponse fieldCapsResponse ) {
176+ Map <String , Integer > indexMappingHashToDuplicateCount = new HashMap <>();
177+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps = new HashMap <>();
178+
168179 for (FieldCapabilitiesIndexResponse response : fieldCapsResponse .getIndexResponses ()) {
169- if (seenHashes . add (response .getIndexMappingHash ()) == false ) {
180+ if (indexMappingHashToDuplicateCount . compute (response .getIndexMappingHash (), ( k , v ) -> v == null ? 1 : v + 1 ) > 1 ) {
170181 continue ;
171182 }
172183 for (IndexFieldCapabilities fc : response .get ().values ()) {
173184 if (fc .isMetadatafield ()) {
174185 // ESQL builds the metadata fields if they are asked for without using the resolution.
175186 continue ;
176187 }
177- List <IndexFieldCapabilities > all = fieldsCaps .computeIfAbsent (fc .name (), (_key ) -> new ArrayList <>());
188+ List <IndexFieldCapabilities > all = fieldsCaps .computeIfAbsent (
189+ fc .name (),
190+ (_key ) -> new IndexFieldCapabilitiesWithSourceHash (new ArrayList <>(), response .getIndexMappingHash ())
191+ ).fieldCapabilities ;
178192 all .add (fc );
179193 }
180194 }
181- return fieldsCaps ;
195+
196+ var iterator = indexMappingHashToDuplicateCount .entrySet ().iterator ();
197+ while (iterator .hasNext ()) {
198+ var next = iterator .next ();
199+ if (next .getValue () <= 1 ) {
200+ iterator .remove ();
201+ } else {
202+ next .setValue (next .getValue () - 1 );
203+ }
204+ }
205+
206+ return new CollectedFieldCaps (fieldsCaps , indexMappingHashToDuplicateCount );
182207 }
183208
184209 private static EsField createField (
0 commit comments