private static TreeMap<String, FieldMapping> getFlattenedFieldMappings(final ElasticIndexDoc elasticIndex,
                                                                           final ElasticsearchClient elasticClient) {
        // Flatten the mappings, which are keyed by index, into a de-duplicated list
        final TreeMap<String, FieldMapping> mappings = new TreeMap<>((o1, o2) -> {
            if (Objects.equals(o1, o2)) {
                return 0;
            }
            if (o2 == null) {
                return 1;
            }

            return o1.compareToIgnoreCase(o2);
        });

        final String indexName = elasticIndex.getIndexName();
        final GetFieldMappingRequest request = GetFieldMappingRequest.of(r -> r
                .expandWildcards(ExpandWildcard.Open)
                .index(indexName)
                .fields("*"));

        try {
            final GetFieldMappingResponse response = elasticClient.indices().getFieldMapping(request);
            final Map<String, TypeFieldMappings> allMappings = response.result();

            // Build a list of all multi fields (i.e. those defined only in the field mapping).
            // These are excluded from the fields the user can pick via the Stroom UI, as they are not part
            // of the returned `_source` field.
            final HashSet<String> multiFieldMappings = new HashSet<>();
            allMappings.values().forEach(indexMappings -> indexMappings.mappings().forEach((fieldName, mapping) -> {
                final Property source = mapping.mapping().get(fieldName);
                if (source != null && source._get() instanceof PropertyBase) {
                    final var multiFields = ((PropertyBase) source._get()).fields();

                    if (!multiFields.isEmpty()) {
                        multiFields.forEach((multiFieldName, multiFieldMapping) -> {
                            final String fullName = mapping.fullName() + "." + multiFieldName;
                            multiFieldMappings.add(fullName);
                        });
                    }
                }
            }));

            allMappings.values().forEach(indexMappings -> indexMappings.mappings().forEach((fieldName, mapping) -> {
                if (!mappings.containsKey(fieldName) && !multiFieldMappings.contains(mapping.fullName())) {
                    mappings.put(fieldName, mapping);
                }
            }));
        } catch (final IOException e) {
            LOGGER.error(e::getMessage, e);
        }

        return mappings;
    }
Tests whether a field has the mapping property `index` set to `true`.
This determines whether it is searchable.
stroom-search/stroom-search-elastic/src/main/java/stroom/search/elastic/search/ElasticSearchProvider.java