@@ -93,13 +93,15 @@ 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
102- Map <String , List <IndexFieldCapabilities >> fieldsCaps = collectFieldCaps (fieldCapsResponse );
102+ var collectedFieldCaps = collectFieldCaps (fieldCapsResponse );
103+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps = collectedFieldCaps .fieldsCaps ;
104+ Map <String , Integer > indexMappingHashDuplicates = collectedFieldCaps .indexMappingHashDuplicates ;
103105
104106 // Build hierarchical fields - it's easier to do it in sorted order so the object fields come first.
105107 // TODO flattened is simpler - could we get away with that?
@@ -131,7 +133,8 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
131133 }
132134 // TODO we're careful to make isAlias match IndexResolver - but do we use it?
133135
134- List <IndexFieldCapabilities > fcs = fieldsCaps .get (fullName );
136+ var fieldCap = fieldsCaps .get (fullName );
137+ List <IndexFieldCapabilities > fcs = fieldCap .fieldCapabilities ;
135138 EsField field = firstUnsupportedParent == null
136139 ? createField (fieldCapsResponse , name , fullName , fcs , isAlias )
137140 : new UnsupportedEsField (
@@ -141,8 +144,7 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
141144 new HashMap <>()
142145 );
143146 fields .put (name , field );
144-
145- var isPartiallyUnmapped = fcs .size () < numberOfIndices ;
147+ var isPartiallyUnmapped = fcs .size () + indexMappingHashDuplicates .getOrDefault (fieldCap .indexMappingHash , 0 ) < numberOfIndices ;
146148 if (isPartiallyUnmapped ) {
147149 partiallyUnmappedFields .add (fullName );
148150 }
@@ -166,23 +168,46 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
166168 return IndexResolution .valid (index , concreteIndices .keySet (), unavailableRemotes );
167169 }
168170
169- private static Map <String , List <IndexFieldCapabilities >> collectFieldCaps (FieldCapabilitiesResponse fieldCapsResponse ) {
170- Set <String > seenHashes = new HashSet <>();
171- Map <String , List <IndexFieldCapabilities >> fieldsCaps = new HashMap <>();
171+ private record IndexFieldCapabilitiesWithSourceHash (List <IndexFieldCapabilities > fieldCapabilities , String indexMappingHash ) {}
172+
173+ private record CollectedFieldCaps (
174+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps ,
175+ // The map won't contain entries without duplicates, i.e., it's number of occurrences - 1.
176+ Map <String , Integer > indexMappingHashDuplicates
177+ ) {}
178+
179+ private static CollectedFieldCaps collectFieldCaps (FieldCapabilitiesResponse fieldCapsResponse ) {
180+ Map <String , Integer > indexMappingHashToDuplicateCount = new HashMap <>();
181+ Map <String , IndexFieldCapabilitiesWithSourceHash > fieldsCaps = new HashMap <>();
182+
172183 for (FieldCapabilitiesIndexResponse response : fieldCapsResponse .getIndexResponses ()) {
173- if (seenHashes . add (response .getIndexMappingHash ()) == false ) {
184+ if (indexMappingHashToDuplicateCount . compute (response .getIndexMappingHash (), ( k , v ) -> v == null ? 1 : v + 1 ) > 1 ) {
174185 continue ;
175186 }
176187 for (IndexFieldCapabilities fc : response .get ().values ()) {
177188 if (fc .isMetadatafield ()) {
178189 // ESQL builds the metadata fields if they are asked for without using the resolution.
179190 continue ;
180191 }
181- List <IndexFieldCapabilities > all = fieldsCaps .computeIfAbsent (fc .name (), (_key ) -> new ArrayList <>());
192+ List <IndexFieldCapabilities > all = fieldsCaps .computeIfAbsent (
193+ fc .name (),
194+ (_key ) -> new IndexFieldCapabilitiesWithSourceHash (new ArrayList <>(), response .getIndexMappingHash ())
195+ ).fieldCapabilities ;
182196 all .add (fc );
183197 }
184198 }
185- return fieldsCaps ;
199+
200+ var iterator = indexMappingHashToDuplicateCount .entrySet ().iterator ();
201+ while (iterator .hasNext ()) {
202+ var next = iterator .next ();
203+ if (next .getValue () <= 1 ) {
204+ iterator .remove ();
205+ } else {
206+ next .setValue (next .getValue () - 1 );
207+ }
208+ }
209+
210+ return new CollectedFieldCaps (fieldsCaps , indexMappingHashToDuplicateCount );
186211 }
187212
188213 private static EsField createField (
0 commit comments