diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts index b4b86b73a5f4..6b26c82dc95e 100644 --- a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_capabilities.ts @@ -25,10 +25,6 @@ import { FieldCapsResponse, readFieldCapsResponse } from './field_caps_response' import { mergeOverrides } from './overrides'; import { FieldDescriptor } from '../../index_patterns_fetcher'; -export function concatIfUniq(arr: T[], value: T) { - return arr.includes(value) ? arr : arr.concat(value); -} - /** * Get the field capabilities for field in `indices`, excluding * all internal/underscore-prefixed fields that are not in `metaFields` @@ -49,8 +45,20 @@ export async function getFieldCapabilities( const allFieldsUnsorted = Object.keys(fieldsFromFieldCapsByName) .filter((name) => !name.startsWith('_')) .concat(metaFields) - .reduce(concatIfUniq, [] as string[]) - .map((name) => + .reduce<{ names: string[]; hash: Record }>( + (agg, value) => { + // This is intentionally using a "hash" and a "push" to be highly optimized with very large indexes + if (agg.hash[value] != null) { + return agg; + } else { + agg.hash[value] = value; + agg.names.push(value); + return agg; + } + }, + { names: [], hash: {} } + ) + .names.map((name) => defaults({}, fieldsFromFieldCapsByName[name], { name, type: 'string', diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.ts b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.ts index cb1ec6a2ebcf..861b92569faf 100644 --- a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.ts +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.ts @@ -93,8 +93,12 @@ export interface FieldCapsResponse { */ export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): FieldDescriptor[] { const capsByNameThenType = fieldCapsResponse.fields; - const kibanaFormattedCaps: FieldDescriptor[] = Object.keys(capsByNameThenType).map( - (fieldName) => { + + const kibanaFormattedCaps = Object.keys(capsByNameThenType).reduce<{ + array: FieldDescriptor[]; + hash: Record; + }>( + (agg, fieldName) => { const capsByType = capsByNameThenType[fieldName]; const types = Object.keys(capsByType); @@ -119,7 +123,7 @@ export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): Fie // ignore the conflict and carry on (my wayward son) const uniqueKibanaTypes = uniq(types.map(castEsToKbnFieldTypeName)); if (uniqueKibanaTypes.length > 1) { - return { + const field = { name: fieldName, type: 'conflict', esTypes: types, @@ -134,10 +138,14 @@ export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): Fie {} ), }; + // This is intentionally using a "hash" and a "push" to be highly optimized with very large indexes + agg.array.push(field); + agg.hash[fieldName] = field; + return agg; } const esType = types[0]; - return { + const field = { name: fieldName, type: castEsToKbnFieldTypeName(esType), esTypes: types, @@ -145,11 +153,19 @@ export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): Fie aggregatable: isAggregatable, readFromDocValues: shouldReadFieldFromDocValues(isAggregatable, esType), }; + // This is intentionally using a "hash" and a "push" to be highly optimized with very large indexes + agg.array.push(field); + agg.hash[fieldName] = field; + return agg; + }, + { + array: [], + hash: {}, } ); // Get all types of sub fields. These could be multi fields or children of nested/object types - const subFields = kibanaFormattedCaps.filter((field) => { + const subFields = kibanaFormattedCaps.array.filter((field) => { return field.name.includes('.'); }); @@ -161,9 +177,9 @@ export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): Fie .map((_, index, parentFieldNameParts) => { return parentFieldNameParts.slice(0, index + 1).join('.'); }); - const parentFieldCaps = parentFieldNames.map((parentFieldName) => { - return kibanaFormattedCaps.find((caps) => caps.name === parentFieldName); - }); + const parentFieldCaps = parentFieldNames.map( + (parentFieldName) => kibanaFormattedCaps.hash[parentFieldName] + ); const parentFieldCapsAscending = parentFieldCaps.reverse(); if (parentFieldCaps && parentFieldCaps.length > 0) { @@ -188,7 +204,7 @@ export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): Fie } }); - return kibanaFormattedCaps.filter((field) => { + return kibanaFormattedCaps.array.filter((field) => { return !['object', 'nested'].includes(field.type); }); }