mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[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:
parent
a67eef4c31
commit
202980e887
30 changed files with 230 additions and 100 deletions
|
@ -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,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -18,4 +18,6 @@ export {
|
|||
BoolQuery,
|
||||
DataViewBase,
|
||||
DataViewFieldBase,
|
||||
IFieldSubTypeMulti,
|
||||
IFieldSubTypeNested,
|
||||
} from './types';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,3 +9,9 @@
|
|||
export * from './es_query';
|
||||
export * from './filters';
|
||||
export * from './kuery';
|
||||
export {
|
||||
isDataViewFieldSubtypeMulti,
|
||||
isDataViewFieldSubtypeNested,
|
||||
getDataViewFieldSubtypeMulti,
|
||||
getDataViewFieldSubtypeNested,
|
||||
} from './utils';
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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}.`,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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 ?? '');
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue