[data views] clarify field subtype typescript types (#112499)

* separate out multi and nested subTypes

* separate out multi and nested subTypes

* add undefined checks

* remove expect error statements

* use helper functions in es-query

* simplify changes with helper functions

* checking existence instead of getting value x2

* simplify types and revert discover changes

* update discover sidebar with helper methods

* try helpers with group_fields file

* try different helper with group_fields file

* revert group field changes, try nested field helpers

* revert nested field changes, try field_name.tsx helpers

* fix maps jest test

* use helpers in discover instead of setting types

* fix field_name.tsx

* Update index_pattern_util.test.ts

* lint  fix

* fix common exports

* reduce data_views plugin bundle size

* reduce data_views plugin bundle size

* remove discover reliance on es-query package

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Matthew Kime 2021-10-06 23:13:21 -05:00 committed by GitHub
parent a67eef4c31
commit 202980e887
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 230 additions and 100 deletions

View file

@ -8,6 +8,7 @@
import { getFilterField, cleanFilter, Filter } from '../filters';
import { IndexPatternBase } from './types';
import { getDataViewFieldSubtypeNested } from '../utils';
/** @internal */
export const handleNestedFilter = (filter: Filter, indexPattern?: IndexPatternBase) => {
@ -21,7 +22,9 @@ export const handleNestedFilter = (filter: Filter, indexPattern?: IndexPatternBa
const field = indexPattern.fields.find(
(indexPatternField) => indexPatternField.name === fieldName
);
if (!field || !field.subType || !field.subType.nested || !field.subType.nested.path) {
const subTypeNested = field && getDataViewFieldSubtypeNested(field);
if (!subTypeNested) {
return filter;
}
@ -31,7 +34,7 @@ export const handleNestedFilter = (filter: Filter, indexPattern?: IndexPatternBa
meta: filter.meta,
query: {
nested: {
path: field.subType.nested.path,
path: subTypeNested.nested.path,
query: query.query || query,
},
},

View file

@ -18,4 +18,6 @@ export {
BoolQuery,
DataViewBase,
DataViewFieldBase,
IFieldSubTypeMulti,
IFieldSubTypeNested,
} from './types';

View file

@ -12,11 +12,24 @@ import type { estypes } from '@elastic/elasticsearch';
* A field's sub type
* @public
*/
export interface IFieldSubType {
export type IFieldSubType = IFieldSubTypeMultiOptional | IFieldSubTypeNestedOptional;
export interface IFieldSubTypeMultiOptional {
multi?: { parent: string };
}
export interface IFieldSubTypeMulti {
multi: { parent: string };
}
export interface IFieldSubTypeNestedOptional {
nested?: { path: string };
}
export interface IFieldSubTypeNested {
nested: { path: string };
}
/**
* A base interface for an index pattern field
* @public

View file

@ -9,3 +9,9 @@
export * from './es_query';
export * from './filters';
export * from './kuery';
export {
isDataViewFieldSubtypeMulti,
isDataViewFieldSubtypeNested,
getDataViewFieldSubtypeMulti,
getDataViewFieldSubtypeNested,
} from './utils';

View file

@ -6,11 +6,11 @@
* Side Public License, v 1.
*/
import { get, isUndefined } from 'lodash';
import { isUndefined } from 'lodash';
import { estypes } from '@elastic/elasticsearch';
import { getPhraseScript } from '../../filters';
import { getFields } from './utils/get_fields';
import { getTimeZoneFromSettings } from '../../utils';
import { getTimeZoneFromSettings, getDataViewFieldSubtypeNested } from '../../utils';
import { getFullFieldNameNode } from './utils/get_full_field_name_node';
import { IndexPatternBase, KueryNode, IndexPatternFieldBase, KueryQueryOptions } from '../..';
@ -105,16 +105,13 @@ export function toElasticsearchQuery(
const wrapWithNestedQuery = (query: any) => {
// Wildcards can easily include nested and non-nested fields. There isn't a good way to let
// users handle this themselves so we automatically add nested queries in this scenario.
if (
!(fullFieldNameArg.type === 'wildcard') ||
!get(field, 'subType.nested') ||
context?.nested
) {
const subTypeNested = getDataViewFieldSubtypeNested(field);
if (!(fullFieldNameArg.type === 'wildcard') || !subTypeNested?.nested || context?.nested) {
return query;
} else {
return {
nested: {
path: field.subType!.nested!.path,
path: subTypeNested.nested.path,
query,
score_mode: 'none',
},

View file

@ -6,24 +6,24 @@
* Side Public License, v 1.
*/
import _ from 'lodash';
import { pick, map, mapValues } from 'lodash';
import { estypes } from '@elastic/elasticsearch';
import { nodeTypes } from '../node_types';
import * as ast from '../ast';
import { getRangeScript, RangeFilterParams } from '../../filters';
import { getFields } from './utils/get_fields';
import { getTimeZoneFromSettings } from '../../utils';
import { getTimeZoneFromSettings, getDataViewFieldSubtypeNested } from '../../utils';
import { getFullFieldNameNode } from './utils/get_full_field_name_node';
import { IndexPatternBase, KueryNode, KueryQueryOptions } from '../..';
export function buildNodeParams(fieldName: string, params: RangeFilterParams) {
const paramsToMap = _.pick(params, 'gt', 'lt', 'gte', 'lte', 'format');
const paramsToMap = pick(params, 'gt', 'lt', 'gte', 'lte', 'format');
const fieldNameArg =
typeof fieldName === 'string'
? ast.fromLiteralExpression(fieldName)
: nodeTypes.literal.buildNode(fieldName);
const args = _.map(paramsToMap, (value: number | string, key: string) => {
const args = map(paramsToMap, (value: number | string, key: string) => {
return nodeTypes.namedArg.buildNode(key, value);
});
@ -46,7 +46,7 @@ export function toElasticsearchQuery(
);
const fields = indexPattern ? getFields(fullFieldNameArg, indexPattern) : [];
const namedArgs = extractArguments(args);
const queryParams = _.mapValues(namedArgs, (arg: KueryNode) => {
const queryParams = mapValues(namedArgs, (arg: KueryNode) => {
return ast.toElasticsearchQuery(arg);
});
@ -67,16 +67,13 @@ export function toElasticsearchQuery(
const wrapWithNestedQuery = (query: any) => {
// Wildcards can easily include nested and non-nested fields. There isn't a good way to let
// users handle this themselves so we automatically add nested queries in this scenario.
if (
!(fullFieldNameArg.type === 'wildcard') ||
!_.get(field, 'subType.nested') ||
context!.nested
) {
const subTypeNested = getDataViewFieldSubtypeNested(field);
if (!(fullFieldNameArg.type === 'wildcard') || !subTypeNested?.nested || context!.nested) {
return query;
} else {
return {
nested: {
path: field.subType!.nested!.path,
path: subTypeNested.nested.path,
query,
score_mode: 'none',
},

View file

@ -8,6 +8,7 @@
import { getFields } from './get_fields';
import { IndexPatternBase, IndexPatternFieldBase, KueryNode } from '../../..';
import { getDataViewFieldSubtypeNested } from '../../../utils';
export function getFullFieldNameNode(
rootNameNode: any,
@ -28,8 +29,8 @@ export function getFullFieldNameNode(
const fields = getFields(fullFieldNameNode, indexPattern);
const errors = fields!.reduce((acc: any, field: IndexPatternFieldBase) => {
const nestedPathFromField =
field.subType && field.subType.nested ? field.subType.nested.path : undefined;
const subTypeNested = getDataViewFieldSubtypeNested(field);
const nestedPathFromField = subTypeNested?.nested.path;
if (nestedPath && !nestedPathFromField) {
return [
@ -48,11 +49,7 @@ export function getFullFieldNameNode(
if (nestedPathFromField !== nestedPath) {
return [
...acc,
`Nested field ${
field.name
} is being queried with the incorrect nested path. The correct path is ${
field.subType!.nested!.path
}.`,
`Nested field ${field.name} is being queried with the incorrect nested path. The correct path is ${subTypeNested?.nested.path}.`,
];
}

View file

@ -7,6 +7,7 @@
*/
import moment from 'moment-timezone';
import { DataViewFieldBase, IFieldSubTypeNested, IFieldSubTypeMulti } from './es_query';
/** @internal */
export function getTimeZoneFromSettings(dateFormatTZ: string) {
@ -14,3 +15,23 @@ export function getTimeZoneFromSettings(dateFormatTZ: string) {
return dateFormatTZ === 'Browser' ? detectedTimezone : dateFormatTZ;
}
type HasSubtype = Pick<DataViewFieldBase, 'subType'>;
export function isDataViewFieldSubtypeNested(field: HasSubtype) {
const subTypeNested = field?.subType as IFieldSubTypeNested;
return !!subTypeNested?.nested?.path;
}
export function getDataViewFieldSubtypeNested(field: HasSubtype) {
return isDataViewFieldSubtypeNested(field) ? (field.subType as IFieldSubTypeNested) : undefined;
}
export function isDataViewFieldSubtypeMulti(field: HasSubtype) {
const subTypeNested = field?.subType as IFieldSubTypeMulti;
return !!subTypeNested?.multi?.parent;
}
export function getDataViewFieldSubtypeMulti(field: HasSubtype) {
return isDataViewFieldSubtypeMulti(field) ? (field.subType as IFieldSubTypeMulti) : undefined;
}

View file

@ -9,7 +9,11 @@
import { useEffect, useRef, useState } from 'react';
import { debounce } from 'lodash';
import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import {
IndexPatternBase,
IndexPatternFieldBase,
getDataViewFieldSubtypeNested,
} from '@kbn/es-query';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715
// import { AutocompleteStart } from '../../../../../../../../src/plugins/data/public';
@ -68,14 +72,13 @@ export const useFieldValueAutocomplete = ({
}
setIsLoading(true);
const field =
fieldSelected.subType != null && fieldSelected.subType.nested != null
? {
...fieldSelected,
name: `${fieldSelected.subType.nested.path}.${fieldSelected.name}`,
}
: fieldSelected;
const subTypeNested = getDataViewFieldSubtypeNested(fieldSelected);
const field = subTypeNested
? {
...fieldSelected,
name: `${subTypeNested.nested.path}.${fieldSelected.name}`,
}
: fieldSelected;
const newSuggestions = await autocompleteService.getValueSuggestions({
field,

View file

@ -28,7 +28,12 @@ import {
exceptionListItemSchema,
nestedEntryItem,
} from '@kbn/securitysolution-io-ts-list-types';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import {
IndexPatternBase,
IndexPatternFieldBase,
getDataViewFieldSubtypeNested,
isDataViewFieldSubtypeNested,
} from '@kbn/es-query';
import {
EXCEPTION_OPERATORS,
@ -297,11 +302,11 @@ export const getFilteredIndexPatterns = (
...indexPatterns,
fields: indexPatterns.fields
.filter((indexField) => {
const subTypeNested = getDataViewFieldSubtypeNested(indexField);
const fieldHasCommonParentPath =
indexField.subType != null &&
indexField.subType.nested != null &&
subTypeNested &&
item.parent != null &&
indexField.subType.nested.path === item.parent.parent.field;
subTypeNested.nested.path === item.parent.parent.field;
return fieldHasCommonParentPath;
})
@ -317,9 +322,7 @@ export const getFilteredIndexPatterns = (
// when user selects to add a nested entry, only nested fields are shown as options
return {
...indexPatterns,
fields: indexPatterns.fields.filter(
(field) => field.subType != null && field.subType.nested != null
),
fields: indexPatterns.fields.filter((field) => isDataViewFieldSubtypeNested(field)),
};
} else {
return indexPatterns;
@ -346,10 +349,8 @@ export const getEntryOnFieldChange = (
// a user selects "exists", as soon as they make a selection
// we can now identify the 'parent' and 'child' this is where
// we first convert the entry into type "nested"
const newParentFieldValue =
newField.subType != null && newField.subType.nested != null
? newField.subType.nested.path
: '';
const subTypeNested = getDataViewFieldSubtypeNested(newField);
const newParentFieldValue = subTypeNested?.nested.path || '';
return {
index: entryIndex,

View file

@ -58,7 +58,6 @@ export {
DATA_VIEW_SAVED_OBJECT_TYPE,
INDEX_PATTERN_SAVED_OBJECT_TYPE,
isFilterable,
isNestedField,
fieldList,
DataViewField,
IndexPatternField,
@ -75,4 +74,8 @@ export {
DuplicateDataViewError,
DataViewSavedObjectConflictError,
getIndexPatternLoadMeta,
isNestedField,
isMultiField,
getFieldSubtypeMulti,
getFieldSubtypeNested,
} from '../../data_views/common';

View file

@ -53,12 +53,9 @@ export const setupGetFieldSuggestions: KqlQuerySuggestionProvider<QuerySuggestio
);
const search = `${prefix}${suffix}`.trim().toLowerCase();
const matchingFields = allFields.filter((field) => {
const subTypeNested = indexPatternsUtils.getFieldSubtypeNested(field);
return (
(!nestedPath ||
(nestedPath &&
field.subType &&
field.subType.nested &&
field.subType.nested.path.includes(nestedPath))) &&
(!nestedPath || (nestedPath && subTypeNested?.nested.path.includes(nestedPath))) &&
field.name.toLowerCase().includes(search)
);
});

View file

@ -38,7 +38,13 @@ export const exporters = {
* Index patterns:
*/
import { isNestedField, isFilterable } from '../common';
import {
isNestedField,
isFilterable,
isMultiField,
getFieldSubtypeNested,
getFieldSubtypeMulti,
} from '../common';
import {
ILLEGAL_CHARACTERS_KEY,
@ -59,6 +65,9 @@ export const indexPatterns = {
ILLEGAL_CHARACTERS,
isFilterable,
isNestedField,
isMultiField,
getFieldSubtypeMulti,
getFieldSubtypeNested,
validate: validateDataView,
flattenHitWrapper,
};

View file

@ -37,7 +37,7 @@ import { QueryLanguageSwitcher } from './language_switcher';
import { PersistedLog, getQueryLog, matchPairs, toUser, fromUser } from '../../query';
import { SuggestionsListSize } from '../typeahead/suggestions_component';
import { SuggestionsComponent } from '..';
import { KIBANA_USER_QUERY_LANGUAGE_KEY } from '../../../common';
import { KIBANA_USER_QUERY_LANGUAGE_KEY, getFieldSubtypeNested } from '../../../common';
export interface QueryStringInputProps {
indexPatterns: Array<IIndexPattern | string>;
@ -425,10 +425,10 @@ export default class QueryStringInputUI extends Component<Props, State> {
};
private handleNestedFieldSyntaxNotification = (suggestion: QuerySuggestion) => {
const subTypeNested = 'field' in suggestion && getFieldSubtypeNested(suggestion.field);
if (
'field' in suggestion &&
suggestion.field.subType &&
suggestion.field.subType.nested &&
subTypeNested &&
subTypeNested.nested &&
!this.services.storage.get('kibana.KQLNestedQuerySyntaxInfoOptOut')
) {
const { notifications, docLinks } = this.services;

View file

@ -10,7 +10,7 @@ import { get, map } from 'lodash';
import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server';
import { estypes } from '@elastic/elasticsearch';
import { ConfigSchema } from '../../config';
import { IFieldType } from '../../common';
import { IFieldType, getFieldSubtypeNested } from '../../common';
import { findIndexPatternById, getFieldByName } from '../data_views';
import { shimAbortSignal } from '../search';
@ -87,14 +87,14 @@ async function getBody(
},
},
};
if (isFieldObject(field) && field.subType && field.subType.nested) {
const subTypeNested = isFieldObject(field) && getFieldSubtypeNested(field);
if (isFieldObject(field) && subTypeNested) {
return {
...body,
aggs: {
nestedSuggestions: {
nested: {
path: field.subType.nested.path,
path: subTypeNested.nested.path,
},
aggs: body.aggs,
},

View file

@ -13,7 +13,13 @@ import { KBN_FIELD_TYPES } from '@kbn/field-types';
import type { RuntimeField } from '../types';
import type { IFieldType } from './types';
import { FieldSpec, DataView } from '..';
import { shortenDottedString } from './utils';
import {
shortenDottedString,
isDataViewFieldSubtypeMulti,
isDataViewFieldSubtypeNested,
getDataViewFieldSubtypeMulti,
getDataViewFieldSubtypeNested,
} from './utils';
/** @public */
export class DataViewField implements IFieldType {
@ -159,6 +165,22 @@ export class DataViewField implements IFieldType {
return this.aggregatable && !notVisualizableFieldTypes.includes(this.spec.type);
}
public isSubtypeNested() {
return isDataViewFieldSubtypeNested(this);
}
public isSubtypeMulti() {
return isDataViewFieldSubtypeMulti(this);
}
public getSubtypeNested() {
return getDataViewFieldSubtypeNested(this);
}
public getSubtypeMulti() {
return getDataViewFieldSubtypeMulti(this);
}
public deleteCount() {
delete this.spec.count;
}

View file

@ -7,6 +7,12 @@
*/
export * from './types';
export { isFilterable, isNestedField } from './utils';
export {
isFilterable,
isNestedField,
isMultiField,
getFieldSubtypeMulti,
getFieldSubtypeNested,
} from './utils';
export * from './field_list';
export * from './data_view_field';

View file

@ -7,6 +7,7 @@
*/
import { getFilterableKbnTypeNames } from '@kbn/field-types';
import { DataViewFieldBase, IFieldSubTypeNested, IFieldSubTypeMulti } from '@kbn/es-query';
import { IFieldType } from './types';
const filterableTypes = getFilterableKbnTypeNames();
@ -19,9 +20,10 @@ export function isFilterable(field: IFieldType): boolean {
);
}
export function isNestedField(field: IFieldType): boolean {
return !!field.subType?.nested;
}
export const isNestedField = isDataViewFieldSubtypeNested;
export const isMultiField = isDataViewFieldSubtypeMulti;
export const getFieldSubtypeMulti = getDataViewFieldSubtypeMulti;
export const getFieldSubtypeNested = getDataViewFieldSubtypeNested;
const DOT_PREFIX_RE = /(.).+?\./g;
@ -34,3 +36,25 @@ const DOT_PREFIX_RE = /(.).+?\./g;
export function shortenDottedString(input: any) {
return typeof input !== 'string' ? input : input.replace(DOT_PREFIX_RE, '$1.');
}
// Note - this code is duplicated from @kbn/es-query
// as importing code adds about 30k to the data_view bundle size
type HasSubtype = Pick<DataViewFieldBase, 'subType'>;
export function isDataViewFieldSubtypeNested(field: HasSubtype) {
const subTypeNested = field?.subType as IFieldSubTypeNested;
return !!subTypeNested?.nested?.path;
}
export function getDataViewFieldSubtypeNested(field: HasSubtype) {
return isDataViewFieldSubtypeNested(field) ? (field.subType as IFieldSubTypeNested) : undefined;
}
export function isDataViewFieldSubtypeMulti(field: HasSubtype) {
const subTypeNested = field?.subType as IFieldSubTypeMulti;
return !!subTypeNested?.multi?.parent;
}
export function getDataViewFieldSubtypeMulti(field: HasSubtype) {
return isDataViewFieldSubtypeMulti(field) ? (field.subType as IFieldSubTypeMulti) : undefined;
}

View file

@ -14,7 +14,16 @@ export {
INDEX_PATTERN_SAVED_OBJECT_TYPE,
} from './constants';
export type { IFieldType, IIndexPatternFieldList } from './fields';
export { isFilterable, isNestedField, fieldList, DataViewField, IndexPatternField } from './fields';
export {
isFilterable,
fieldList,
DataViewField,
IndexPatternField,
isNestedField,
isMultiField,
getFieldSubtypeMulti,
getFieldSubtypeNested,
} from './fields';
export type {
FieldFormatMap,
RuntimeType,

View file

@ -30,7 +30,10 @@ import { DiscoverIndexPattern } from './discover_index_pattern';
import { DiscoverFieldSearch } from './discover_field_search';
import { FIELDS_LIMIT_SETTING } from '../../../../../../common';
import { groupFields } from './lib/group_fields';
import { IndexPatternField } from '../../../../../../../data/public';
import {
IndexPatternField,
indexPatterns as indexPatternUtils,
} from '../../../../../../../data/public';
import { getDetails } from './lib/get_details';
import { FieldFilterState, getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter';
import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list';
@ -208,7 +211,8 @@ export function DiscoverSidebarComponent({
}
const map = new Map<string, Array<{ field: IndexPatternField; isSelected: boolean }>>();
fields.forEach((field) => {
const parent = field.spec?.subType?.multi?.parent;
const subTypeMulti = indexPatternUtils.getFieldSubtypeMulti(field);
const parent = subTypeMulti?.multi.parent;
if (!parent) {
return;
}

View file

@ -8,6 +8,7 @@
import { IndexPatternField } from 'src/plugins/data/public';
import { FieldFilterState, isFieldFiltered } from './field_filter';
import { getFieldSubtypeMulti } from '../../../../../../../../data/common';
interface GroupedFields {
selected: IndexPatternField[];
@ -54,7 +55,8 @@ export function groupFields(
if (!isFieldFiltered(field, fieldFilterState, fieldCounts)) {
continue;
}
const isSubfield = useNewFieldsApi && field.spec?.subType?.multi?.parent;
const subTypeMulti = getFieldSubtypeMulti(field?.spec);
const isSubfield = useNewFieldsApi && subTypeMulti;
if (columns.includes(field.name)) {
result.selected.push(field);
} else if (popular.includes(field.name) && field.type !== '_source') {

View file

@ -8,6 +8,7 @@
import { escapeRegExp } from 'lodash/fp';
import type { IndexPattern } from 'src/plugins/data/public';
import { getFieldSubtypeNested } from '../../../../../../data/common';
/**
* This function checks if the given field in a given index pattern is a nested field's parent.
@ -51,7 +52,8 @@ export function isNestedFieldParent(fieldName: string, indexPattern: IndexPatter
!!indexPattern.fields.getAll().find((patternField) => {
// We only want to match a full path segment
const nestedRootRegex = new RegExp(escapeRegExp(fieldName) + '(\\.|$)');
return nestedRootRegex.test(patternField.subType?.nested?.path ?? '');
const subTypeNested = getFieldSubtypeNested(patternField);
return nestedRootRegex.test(subTypeNested?.nested.path ?? '');
})
);
}

View file

@ -14,6 +14,7 @@ import { i18n } from '@kbn/i18n';
import { FieldIcon, FieldIconProps } from '../../../../../kibana_react/public';
import { getFieldTypeName } from './field_type_name';
import { IndexPatternField } from '../../../../../data/public';
import { getFieldSubtypeMulti } from '../../../../../data/common';
interface Props {
fieldName: string;
@ -34,7 +35,8 @@ export function FieldName({
const displayName =
fieldMapping && fieldMapping.displayName ? fieldMapping.displayName : fieldName;
const tooltip = displayName !== fieldName ? `${fieldName} (${displayName})` : fieldName;
const isMultiField = !!fieldMapping?.spec?.subType?.multi;
const subTypeMulti = fieldMapping && getFieldSubtypeMulti(fieldMapping.spec);
const isMultiField = !!subTypeMulti?.multi;
return (
<Fragment>

View file

@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { IndexPattern } from '../../../../data/common';
import { IndexPattern, getFieldSubtypeMulti } from '../../../../data/common';
export const getFieldsToShow = (
fields: string[],
@ -16,13 +16,15 @@ export const getFieldsToShow = (
const mapping = (name: string) => indexPattern.fields.getByName(name);
fields.forEach((key) => {
const mapped = mapping(key);
if (mapped && mapped.spec?.subType?.multi?.parent) {
childParentFieldsMap[mapped.name] = mapped.spec.subType.multi.parent;
const subTypeMulti = mapped && getFieldSubtypeMulti(mapped.spec);
if (mapped && subTypeMulti?.multi?.parent) {
childParentFieldsMap[mapped.name] = subTypeMulti.multi.parent;
}
});
return fields.filter((key: string) => {
const fieldMapping = mapping(key);
const isMultiField = !!fieldMapping?.spec?.subType?.multi;
const subTypeMulti = fieldMapping && getFieldSubtypeMulti(fieldMapping.spec);
const isMultiField = !!subTypeMulti?.multi;
if (!isMultiField) {
return true;
}

View file

@ -178,7 +178,7 @@ export class TestScript extends Component<TestScriptProps, TestScriptState> {
this.props.indexPattern.fields
.getAll()
.filter((field) => {
const isMultiField = field.subType && field.subType.multi;
const isMultiField = field.isSubtypeMulti();
return !field.name.startsWith('_') && !isMultiField && !field.scripted;
})
.forEach((field) => {

View file

@ -18,23 +18,30 @@ import { IndexPatternField } from 'src/plugins/data/public';
describe('getSourceFields', () => {
test('Should remove multi fields from field list', () => {
const fields = [
{
name: 'agent',
type: 'string',
} as IndexPatternField,
{
name: 'agent.keyword',
subType: {
multi: {
parent: 'agent',
},
const agent = new IndexPatternField({
name: 'agent',
searchable: true,
aggregatable: true,
type: 'string',
});
const agentKeyword = new IndexPatternField({
name: 'agent.keyword',
subType: {
multi: {
parent: 'agent',
},
type: 'string',
} as IndexPatternField,
];
},
searchable: true,
aggregatable: true,
type: 'string',
});
const fields = [agent, agentKeyword];
const sourceFields = getSourceFields(fields);
expect(sourceFields).toEqual([{ name: 'agent', type: 'string' }]);
expect(sourceFields.length).toEqual(1);
expect(sourceFields[0].name).toEqual('agent');
expect(sourceFields[0].type).toEqual('string');
});
});

View file

@ -98,7 +98,6 @@ export function supportsGeoTileAgg(field?: IndexPatternField): boolean {
export function getSourceFields(fields: IndexPatternField[]): IndexPatternField[] {
return fields.filter((field) => {
// Multi fields are not stored in _source and only exist in index.
const isMultiField = field.subType && field.subType.multi;
return !isMultiField && !indexPatterns.isNestedField(field);
return !field.isSubtypeMulti() && !field.isSubtypeNested();
});
}

View file

@ -32,7 +32,7 @@ export const FieldNameCell = React.memo(
// TODO: Remove. This is what was used to show the plaintext fieldName vs the tooltip one
// const showPlainTextName =
// (data.isObjectArray && data.type !== 'geo_point') || fieldFromBrowserField == null;
const isMultiField = !!fieldMapping?.spec?.subType?.multi;
const isMultiField = fieldMapping?.isSubtypeMulti();
return (
<>
<EuiFlexItem grow={false} className="eventFieldsTable__fieldIcon">

View file

@ -8,6 +8,7 @@
import { noop } from 'lodash/fp';
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { isDataViewFieldSubtypeNested } from '@kbn/es-query';
import { ColumnHeaderOptions } from '../../../../../../../common';
import {
@ -86,7 +87,7 @@ export const HeaderComponent: React.FC<Props> = ({
const { isLoading } = useDeepEqualSelector(
(state) => getManageTimeline(state, timelineId) || { isLoading: false }
);
const showSortingCapability = !isEqlOn && !(header.subType && header.subType.nested);
const showSortingCapability = !isEqlOn && !isDataViewFieldSubtypeNested(header);
return (
<>

View file

@ -7,6 +7,7 @@
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { isDataViewFieldSubtypeNested } from '@kbn/es-query';
import type { ColumnHeaderOptions } from '../../../../../../common/types/timeline';
import type { Sort } from '../../sort';
@ -68,7 +69,7 @@ export const HeaderComponent: React.FC<Props> = ({ header, sort, timelineId }) =
const getManageTimeline = useMemo(() => tGridSelectors.getManageTimelineById(), []);
const { isLoading } = useDeepEqualSelector((state) => getManageTimeline(state, timelineId ?? ''));
const showSortingCapability = !(header.subType && header.subType.nested);
const showSortingCapability = !isDataViewFieldSubtypeNested(header);
return (
<>