@@ -93,14 +93,16 @@ public void resolveAsMergedMapping(
9393
9494 // public for testing only
9595 public static IndexResolution mergedMappings (String indexPattern , FieldCapabilitiesResponse fieldCapsResponse ) {
96- var numberOfIndices = fieldCapsResponse .getIndexResponses ().size ();
9796 assert ThreadPool .assertCurrentThreadPool (ThreadPool .Names .SEARCH_COORDINATION ); // too expensive to run this on a transport worker
97+ var numberOfIndices = fieldCapsResponse .getIndexResponses ().size ();
9898 if (fieldCapsResponse .getIndexResponses ().isEmpty ()) {
9999 return IndexResolution .notFound (indexPattern );
100100 }
101101
102102 // For each field name, store a list of the field caps responses from each index
103- Map <String , List <IndexFieldCapabilities >> fieldsCaps = collectFieldCaps (fieldCapsResponse );
103+ var collectedFieldCaps = collectFieldCaps (fieldCapsResponse );
104+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps = collectedFieldCaps .fieldsCaps ;
105+ Map <String , Integer > indexMappingHashDuplicates = collectedFieldCaps .indexMappingHashDuplicates ;
104106
105107 // Build hierarchical fields - it's easier to do it in sorted order so the object fields come first.
106108 // TODO flattened is simpler - could we get away with that?
@@ -133,7 +135,8 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
133135 }
134136 // TODO we're careful to make isAlias match IndexResolver - but do we use it?
135137
136- List <IndexFieldCapabilities > fcs = fieldsCaps .get (fullName );
138+ var fieldCap = fieldsCaps .get (fullName );
139+ List <IndexFieldCapabilities > fcs = fieldCap .fieldCapabilities ;
137140 EsField field = firstUnsupportedParent == null
138141 ? createField (fieldCapsResponse , name , fullName , fcs , isAlias )
139142 : new UnsupportedEsField (
@@ -143,8 +146,7 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
143146 new HashMap <>()
144147 );
145148 fields .put (name , field );
146-
147- var isPartiallyUnmapped = fcs .size () < numberOfIndices ;
149+ var isPartiallyUnmapped = fcs .size () + indexMappingHashDuplicates .getOrDefault (fieldCap .indexMappingHash , 0 ) < numberOfIndices ;
148150 if (isPartiallyUnmapped ) {
149151 partiallyUnmappedFields .add (fullName );
150152 }
@@ -170,23 +172,46 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
170172 return IndexResolution .valid (index , concreteIndices .keySet (), failures );
171173 }
172174
173- private static Map <String , List <IndexFieldCapabilities >> collectFieldCaps (FieldCapabilitiesResponse fieldCapsResponse ) {
174- Set <String > seenHashes = new HashSet <>();
175- Map <String , List <IndexFieldCapabilities >> fieldsCaps = new HashMap <>();
175+ private record IndexFieldCapabilitiesWithSourceHash (List <IndexFieldCapabilities > fieldCapabilities , String indexMappingHash ) {}
176+
177+ private record CollectedFieldCaps (
178+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps ,
179+ // The map won't contain entries without duplicates, i.e., it's number of occurrences - 1.
180+ Map <String , Integer > indexMappingHashDuplicates
181+ ) {}
182+
183+ private static CollectedFieldCaps collectFieldCaps (FieldCapabilitiesResponse fieldCapsResponse ) {
184+ Map <String , Integer > indexMappingHashToDuplicateCount = new HashMap <>();
185+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps = new HashMap <>();
186+
176187 for (FieldCapabilitiesIndexResponse response : fieldCapsResponse .getIndexResponses ()) {
177- if (seenHashes . add (response .getIndexMappingHash ()) == false ) {
188+ if (indexMappingHashToDuplicateCount . compute (response .getIndexMappingHash (), ( k , v ) -> v == null ? 1 : v + 1 ) > 1 ) {
178189 continue ;
179190 }
180191 for (IndexFieldCapabilities fc : response .get ().values ()) {
181192 if (fc .isMetadatafield ()) {
182193 // ESQL builds the metadata fields if they are asked for without using the resolution.
183194 continue ;
184195 }
185- List <IndexFieldCapabilities > all = fieldsCaps .computeIfAbsent (fc .name (), (_key ) -> new ArrayList <>());
196+ List <IndexFieldCapabilities > all = fieldsCaps .computeIfAbsent (
197+ fc .name (),
198+ (_key ) -> new IndexFieldCapabilitiesWithSourceHash (new ArrayList <>(), response .getIndexMappingHash ())
199+ ).fieldCapabilities ;
186200 all .add (fc );
187201 }
188202 }
189- return fieldsCaps ;
203+
204+ var iterator = indexMappingHashToDuplicateCount .entrySet ().iterator ();
205+ while (iterator .hasNext ()) {
206+ var next = iterator .next ();
207+ if (next .getValue () <= 1 ) {
208+ iterator .remove ();
209+ } else {
210+ next .setValue (next .getValue () - 1 );
211+ }
212+ }
213+
214+ return new CollectedFieldCaps (fieldsCaps , indexMappingHashToDuplicateCount );
190215 }
191216
192217 private static EsField createField (
0 commit comments