diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md new file mode 100644 index 0000000000000..b0e1266ad2997 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [addRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md) + +## IndexPattern.addRuntimeComposite() method + +Create a runtime composite and add its subFields to the index pattern fields list + +Signature: + +```typescript +addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | The runtime composite name | +| runtimeComposite | RuntimeCompositeWithSubFields | The runtime composite definition | + +Returns: + +`IndexPatternField[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md index 5640395139ba6..1561892e2c47e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md @@ -9,7 +9,7 @@ Add a runtime field - Appended to existing mapped field or a new field is create Signature: ```typescript -addRuntimeField(name: string, runtimeField: RuntimeField): void; +addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; ``` ## Parameters @@ -17,9 +17,9 @@ addRuntimeField(name: string, runtimeField: RuntimeField): void; | Parameter | Type | Description | | --- | --- | --- | | name | string | | -| runtimeField | RuntimeField | | +| enhancedRuntimeField | EnhancedRuntimeField | | Returns: -`void` +`IndexPatternField` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md index 37d31a35167df..390a85ed4c288 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md @@ -14,7 +14,9 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; ``` Returns: @@ -26,6 +28,8 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md new file mode 100644 index 0000000000000..1e5db3903e43a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md) + +## IndexPattern.getRuntimeComposite() method + +Returns runtime composite if exists + +Signature: + +```typescript +getRuntimeComposite(name: string): RuntimeComposite | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeComposite | null` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md new file mode 100644 index 0000000000000..22f91d6695ce3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getRuntimeCompositeWithSubFields](./kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md) + +## IndexPattern.getRuntimeCompositeWithSubFields() method + +Returns runtime composite (if exists) with its subFields + +Signature: + +```typescript +getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeCompositeWithSubFields | null` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md index 51ca42fdce70a..1c01effbee127 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md @@ -45,7 +45,8 @@ export declare class IndexPattern implements IIndexPattern | Method | Modifiers | Description | | --- | --- | --- | -| [addRuntimeField(name, runtimeField)](./kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | +| [addRuntimeComposite(name, runtimeComposite)](./kibana-plugin-plugins-data-public.indexpattern.addruntimecomposite.md) | | Create a runtime composite and add its subFields to the index pattern fields list | +| [addRuntimeField(name, enhancedRuntimeField)](./kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | | [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md) | | Add scripted field to field list | | [getAggregationRestrictions()](./kibana-plugin-plugins-data-public.indexpattern.getaggregationrestrictions.md) | | | | [getAsSavedObjectBody()](./kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | @@ -54,6 +55,8 @@ export declare class IndexPattern implements IIndexPattern | [getFormatterForField(field)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | | [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [getNonScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md) | | | +| [getRuntimeComposite(name)](./kibana-plugin-plugins-data-public.indexpattern.getruntimecomposite.md) | | Returns runtime composite if exists | +| [getRuntimeCompositeWithSubFields(name)](./kibana-plugin-plugins-data-public.indexpattern.getruntimecompositewithsubfields.md) | | Returns runtime composite (if exists) with its subFields | | [getRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.getruntimefield.md) | | Returns runtime field if exists | | [getScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md) | | | | [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | @@ -61,6 +64,7 @@ export declare class IndexPattern implements IIndexPattern | [hasRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.hasruntimefield.md) | | Checks if runtime field exists | | [isTimeBased()](./kibana-plugin-plugins-data-public.indexpattern.istimebased.md) | | | | [isTimeNanosBased()](./kibana-plugin-plugins-data-public.indexpattern.istimenanosbased.md) | | | +| [removeRuntimeComposite(name)](./kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md) | | Remove a runtime composite with its associated subFields | | [removeRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes. | | [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | | [replaceAllRuntimeFields(newFields)](./kibana-plugin-plugins-data-public.indexpattern.replaceallruntimefields.md) | | Replaces all existing runtime fields with new fields | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md new file mode 100644 index 0000000000000..3fca980ad5d69 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [removeRuntimeComposite](./kibana-plugin-plugins-data-public.indexpattern.removeruntimecomposite.md) + +## IndexPattern.removeRuntimeComposite() method + +Remove a runtime composite with its associated subFields + +Signature: + +```typescript +removeRuntimeComposite(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | Runtime composite name to remove | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md index 41a4d3c55694b..115f86773b629 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md @@ -21,6 +21,7 @@ export interface IndexPatternAttributes | [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldformatmap.md) | string | | | [fields](./kibana-plugin-plugins-data-public.indexpatternattributes.fields.md) | string | | | [intervalName](./kibana-plugin-plugins-data-public.indexpatternattributes.intervalname.md) | string | | +| [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md) | string | | | [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimefieldmap.md) | string | | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternattributes.sourcefilters.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternattributes.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md new file mode 100644 index 0000000000000..40e939013f157 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimecompositemap.md) + +## IndexPatternAttributes.runtimeCompositeMap property + +Signature: + +```typescript +runtimeCompositeMap?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md index ae514e3fc6a8a..a2a8482af2c61 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md @@ -22,6 +22,7 @@ export interface IndexPatternSpec | [fields](./kibana-plugin-plugins-data-public.indexpatternspec.fields.md) | IndexPatternFieldMap | | | [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) | string | saved object id | | [intervalName](./kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md) | string | | +| [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md) | Record<string, RuntimeComposite> | | | [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimefieldmap.md) | Record<string, RuntimeField> | | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md) | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md new file mode 100644 index 0000000000000..4667de1abdd9b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimecompositemap.md) + +## IndexPatternSpec.runtimeCompositeMap property + +Signature: + +```typescript +runtimeCompositeMap?: Record; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 7548aa62eb313..0942f3eb15cdb 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -176,6 +176,7 @@ | [RangeFilterMeta](./kibana-plugin-plugins-data-public.rangefiltermeta.md) | | | [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) | | | [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) | | +| [RuntimeType](./kibana-plugin-plugins-data-public.runtimetype.md) | | | [SavedQueryTimeFilter](./kibana-plugin-plugins-data-public.savedquerytimefilter.md) | | | [SearchBarProps](./kibana-plugin-plugins-data-public.searchbarprops.md) | | | [StatefulSearchBarProps](./kibana-plugin-plugins-data-public.statefulsearchbarprops.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md new file mode 100644 index 0000000000000..35d8d42ba7ab3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.runtimetype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RuntimeType](./kibana-plugin-plugins-data-public.runtimetype.md) + +## RuntimeType type + +Signature: + +```typescript +export declare type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md new file mode 100644 index 0000000000000..a5dfa4fb50ba4 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [addRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md) + +## IndexPattern.addRuntimeComposite() method + +Create a runtime composite and add its subFields to the index pattern fields list + +Signature: + +```typescript +addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | The runtime composite name | +| runtimeComposite | RuntimeCompositeWithSubFields | The runtime composite definition | + +Returns: + +`IndexPatternField[]` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md index ebd7f46d3598e..dcb1b02b653ba 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md @@ -9,7 +9,7 @@ Add a runtime field - Appended to existing mapped field or a new field is create Signature: ```typescript -addRuntimeField(name: string, runtimeField: RuntimeField): void; +addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; ``` ## Parameters @@ -17,9 +17,9 @@ addRuntimeField(name: string, runtimeField: RuntimeField): void; | Parameter | Type | Description | | --- | --- | --- | | name | string | | -| runtimeField | RuntimeField | | +| enhancedRuntimeField | EnhancedRuntimeField | | Returns: -`void` +`IndexPatternField` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md index 0030adf1261e4..580401a955d24 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md @@ -14,7 +14,9 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; ``` Returns: @@ -26,6 +28,8 @@ getComputedFields(): { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md new file mode 100644 index 0000000000000..7594ef8cdb804 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md) + +## IndexPattern.getRuntimeComposite() method + +Returns runtime composite if exists + +Signature: + +```typescript +getRuntimeComposite(name: string): RuntimeComposite | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeComposite | null` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md new file mode 100644 index 0000000000000..8b6cda97ebb50 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getRuntimeCompositeWithSubFields](./kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md) + +## IndexPattern.getRuntimeCompositeWithSubFields() method + +Returns runtime composite (if exists) with its subFields + +Signature: + +```typescript +getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`RuntimeCompositeWithSubFields | null` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md index 27b8a31a2582b..b20765ce5d46c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md @@ -45,7 +45,8 @@ export declare class IndexPattern implements IIndexPattern | Method | Modifiers | Description | | --- | --- | --- | -| [addRuntimeField(name, runtimeField)](./kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | +| [addRuntimeComposite(name, runtimeComposite)](./kibana-plugin-plugins-data-server.indexpattern.addruntimecomposite.md) | | Create a runtime composite and add its subFields to the index pattern fields list | +| [addRuntimeField(name, enhancedRuntimeField)](./kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | | [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md) | | Add scripted field to field list | | [getAggregationRestrictions()](./kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md) | | | | [getAsSavedObjectBody()](./kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | @@ -54,6 +55,8 @@ export declare class IndexPattern implements IIndexPattern | [getFormatterForField(field)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | | [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [getNonScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md) | | | +| [getRuntimeComposite(name)](./kibana-plugin-plugins-data-server.indexpattern.getruntimecomposite.md) | | Returns runtime composite if exists | +| [getRuntimeCompositeWithSubFields(name)](./kibana-plugin-plugins-data-server.indexpattern.getruntimecompositewithsubfields.md) | | Returns runtime composite (if exists) with its subFields | | [getRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.getruntimefield.md) | | Returns runtime field if exists | | [getScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md) | | | | [getSourceFiltering()](./kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | @@ -61,6 +64,7 @@ export declare class IndexPattern implements IIndexPattern | [hasRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.hasruntimefield.md) | | Checks if runtime field exists | | [isTimeBased()](./kibana-plugin-plugins-data-server.indexpattern.istimebased.md) | | | | [isTimeNanosBased()](./kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md) | | | +| [removeRuntimeComposite(name)](./kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md) | | Remove a runtime composite with its associated subFields | | [removeRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate. Doesn't clear associated field attributes. | | [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | | [replaceAllRuntimeFields(newFields)](./kibana-plugin-plugins-data-server.indexpattern.replaceallruntimefields.md) | | Replaces all existing runtime fields with new fields | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md new file mode 100644 index 0000000000000..9cf8385152057 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [removeRuntimeComposite](./kibana-plugin-plugins-data-server.indexpattern.removeruntimecomposite.md) + +## IndexPattern.removeRuntimeComposite() method + +Remove a runtime composite with its associated subFields + +Signature: + +```typescript +removeRuntimeComposite(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | Runtime composite name to remove | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md index 20af97ecc8761..4520383338182 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md @@ -21,6 +21,7 @@ export interface IndexPatternAttributes | [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldformatmap.md) | string | | | [fields](./kibana-plugin-plugins-data-server.indexpatternattributes.fields.md) | string | | | [intervalName](./kibana-plugin-plugins-data-server.indexpatternattributes.intervalname.md) | string | | +| [runtimeCompositeMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md) | string | | | [runtimeFieldMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimefieldmap.md) | string | | | [sourceFilters](./kibana-plugin-plugins-data-server.indexpatternattributes.sourcefilters.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-server.indexpatternattributes.timefieldname.md) | string | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md new file mode 100644 index 0000000000000..6ecf4854873ef --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) > [runtimeCompositeMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimecompositemap.md) + +## IndexPatternAttributes.runtimeCompositeMap property + +Signature: + +```typescript +runtimeCompositeMap?: string; +``` diff --git a/src/plugins/data/common/index_patterns/constants.ts b/src/plugins/data/common/index_patterns/constants.ts index 67e266dbd84a2..43e41832e1770 100644 --- a/src/plugins/data/common/index_patterns/constants.ts +++ b/src/plugins/data/common/index_patterns/constants.ts @@ -14,6 +14,7 @@ export const RUNTIME_FIELD_TYPES = [ 'ip', 'boolean', 'geo_point', + 'composite', ] as const; /** diff --git a/src/plugins/data/common/index_patterns/fields/field_list.ts b/src/plugins/data/common/index_patterns/fields/field_list.ts index 40503e6dbc1b1..2183b023b62a3 100644 --- a/src/plugins/data/common/index_patterns/fields/field_list.ts +++ b/src/plugins/data/common/index_patterns/fields/field_list.ts @@ -15,7 +15,7 @@ import { IndexPattern } from '../index_patterns'; type FieldMap = Map; export interface IIndexPatternFieldList extends Array { - add(field: FieldSpec): void; + add(field: FieldSpec): IndexPatternField; getAll(): IndexPatternField[]; getByName(name: IndexPatternField['name']): IndexPatternField | undefined; getByType(type: IndexPatternField['type']): IndexPatternField[]; @@ -57,11 +57,12 @@ export const fieldList = ( public readonly getByType = (type: IndexPatternField['type']) => [ ...(this.groups.get(type) || new Map()).values(), ]; - public readonly add = (field: FieldSpec) => { + public readonly add = (field: FieldSpec): IndexPatternField => { const newField = new IndexPatternField({ ...field, shortDotsEnable }); this.push(newField); this.setByName(newField); this.setByGroup(newField); + return newField; }; public readonly remove = (field: IFieldType) => { diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap index 1c6b57f70071b..84ad0e4524399 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap @@ -784,6 +784,7 @@ Object { "type": "keyword", }, }, + "runtimeObjectMap": Object {}, "sourceFilters": undefined, "timeFieldName": "time", "title": "title", diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 48bcdf6982b67..ba514195886c3 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -7,8 +7,16 @@ */ import _, { each, reject } from 'lodash'; +import { castEsToKbnFieldTypeName } from '@kbn/field-types'; + import { FieldAttrs, FieldAttrSet, IndexPatternAttributes } from '../..'; -import type { RuntimeField } from '../types'; +import type { + EnhancedRuntimeField, + RuntimeField, + RuntimeType, + RuntimeComposite, + RuntimeCompositeWithSubFields, +} from '../types'; import { DuplicateField } from '../../../../kibana_utils/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common'; @@ -18,7 +26,6 @@ import { flattenHitWrapper } from './flatten_hit'; import { FieldFormatsStartCommon, FieldFormat } from '../../../../field_formats/common'; import { IndexPatternSpec, TypeMeta, SourceFilter, IndexPatternFieldMap } from '../types'; import { SerializedFieldFormat } from '../../../../expressions/common'; -import { castEsToKbnFieldTypeName } from '../../kbn_field_types'; interface IndexPatternDeps { spec?: IndexPatternSpec; @@ -78,6 +85,7 @@ export class IndexPattern implements IIndexPattern { private fieldFormats: FieldFormatsStartCommon; private fieldAttrs: FieldAttrs; private runtimeFieldMap: Record; + private runtimeCompositeMap: Record; /** * prevents errors when index pattern exists before indices @@ -121,6 +129,7 @@ export class IndexPattern implements IIndexPattern { this.intervalName = spec.intervalName; this.allowNoIndex = spec.allowNoIndex || false; this.runtimeFieldMap = spec.runtimeFieldMap || {}; + this.runtimeCompositeMap = spec.runtimeCompositeMap || {}; } /** @@ -195,14 +204,105 @@ export class IndexPattern implements IIndexPattern { }; }); + const runtimeFields = { + ...this.getComputedRuntimeFields(), + ...this.getComputedRuntimeComposites(), + }; + return { storedFields: ['*'], scriptFields, docvalueFields, - runtimeFields: this.runtimeFieldMap, + runtimeFields, }; } + /** + * Method to aggregate all the runtime fields which are **not** created + * from a parent composite runtime field. + * @returns A map of runtime fields + */ + private getComputedRuntimeFields(): Record { + return Object.entries(this.runtimeFieldMap).reduce((acc, [name, field]) => { + const { type, script, parent } = field; + + if (parent !== undefined) { + return acc; + } + + const runtimeFieldRequest: RuntimeField = { + type, + script, + }; + + return { + ...acc, + [name]: runtimeFieldRequest, + }; + }, {}); + } + + /** + * This method reads all the runtime composite fields + * and aggregate the subFields + * + * { + * "compositeName": { + * "type": "composite", + * "script": "emit(...)" // script that emits multiple values + * "fields": { // map of subFields available in the Query + * "field_1": { + * "type": "keyword" + * }, + * "field_2": { + * "type": "ip" + * }, + * } + * } + * } + * + * @returns A map of runtime fields + */ + private getComputedRuntimeComposites(): Record { + return Object.entries(this.runtimeCompositeMap).reduce((acc, [name, runtimeComposite]) => { + const { script, subFields } = runtimeComposite; + + // Aggregate all the subFields belonging to this runtimeComposite + const fields: Record = subFields.reduce( + (accFields, subFieldName) => { + const subField = this.getRuntimeField(`${name}.${subFieldName}`); + + if (!subField) { + return accFields; + } + + return { + ...accFields, + [subFieldName]: { type: subField.type }, + }; + }, + {} as Record + ); + + if (Object.keys(fields).length === 0) { + // This should never happen, but sending a composite runtime field + // with an empty "fields" will break the Query + return acc; + } + + const runtimeFieldRequest: RuntimeField = { + type: 'composite', + script, + fields, + }; + + return { + ...acc, + [name]: runtimeFieldRequest, + }; + }, {}); + } + /** * Create static representation of index pattern */ @@ -219,6 +319,7 @@ export class IndexPattern implements IIndexPattern { type: this.type, fieldFormats: this.fieldFormatMap, runtimeFieldMap: this.runtimeFieldMap, + runtimeCompositeMap: this.runtimeCompositeMap, fieldAttrs: this.fieldAttrs, intervalName: this.intervalName, allowNoIndex: this.allowNoIndex, @@ -329,6 +430,7 @@ export class IndexPattern implements IIndexPattern { : JSON.stringify(this.fieldFormatMap); const fieldAttrs = this.getFieldAttrs(); const runtimeFieldMap = this.runtimeFieldMap; + const runtimeCompositeMap = this.runtimeCompositeMap; return { fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined, @@ -342,6 +444,7 @@ export class IndexPattern implements IIndexPattern { typeMeta: JSON.stringify(this.typeMeta ?? {}), allowNoIndex: this.allowNoIndex ? this.allowNoIndex : undefined, runtimeFieldMap: runtimeFieldMap ? JSON.stringify(runtimeFieldMap) : undefined, + runtimeCompositeMap: runtimeCompositeMap ? JSON.stringify(runtimeCompositeMap) : undefined, }; } @@ -369,22 +472,38 @@ export class IndexPattern implements IIndexPattern { * @param name Field name * @param runtimeField Runtime field definition */ - addRuntimeField(name: string, runtimeField: RuntimeField) { + addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField { + const { type, script, parent, customLabel, format, popularity } = enhancedRuntimeField; + + const runtimeField: RuntimeField = { type, script, parent }; + + let fieldCreated: IndexPatternField; const existingField = this.getFieldByName(name); if (existingField) { existingField.runtimeField = runtimeField; + fieldCreated = existingField; } else { - this.fields.add({ + fieldCreated = this.fields.add({ name, runtimeField, - type: castEsToKbnFieldTypeName(runtimeField.type), + type: castEsToKbnFieldTypeName(type), aggregatable: true, searchable: true, - count: 0, + count: popularity ?? 0, readFromDocValues: false, }); } this.runtimeFieldMap[name] = runtimeField; + + this.setFieldCustomLabel(name, customLabel); + + if (format) { + this.setFieldFormat(name, format); + } else { + this.deleteFieldFormat(name); + } + + return fieldCreated; } /** @@ -436,6 +555,104 @@ export class IndexPattern implements IIndexPattern { delete this.runtimeFieldMap[name]; } + /** + * Create a runtime composite and add its subFields to the index pattern fields list + * @param name - The runtime composite name + * @param runtimeComposite - The runtime composite definition + */ + addRuntimeComposite( + name: string, + runtimeComposite: RuntimeCompositeWithSubFields + ): IndexPatternField[] { + if (!runtimeComposite.subFields || Object.keys(runtimeComposite.subFields).length === 0) { + throw new Error(`Can't save runtime composite [name = ${name}] without subfields.`); + } + + this.removeRuntimeComposite(name); + + const { script, subFields } = runtimeComposite; + + const fieldsCreated: IndexPatternField[] = []; + + for (const [subFieldName, subField] of Object.entries(subFields)) { + const field = this.addRuntimeField(`${name}.${subFieldName}`, { ...subField, parent: name }); + fieldsCreated.push(field); + } + + this.runtimeCompositeMap[name] = { + name, + script, + // We only need to keep a reference of the subFields names + subFields: Object.keys(subFields), + }; + + return fieldsCreated; + } + + /** + * Returns runtime composite if exists + * @param name + */ + getRuntimeComposite(name: string): RuntimeComposite | null { + return this.runtimeCompositeMap[name] ?? null; + } + + /** + * Returns runtime composite (if exists) with its subFields + * @param name + */ + getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null { + const existingRuntimeComposite = this.runtimeCompositeMap[name]; + + if (!existingRuntimeComposite) { + return null; + } + + const subFields = existingRuntimeComposite.subFields.reduce((acc, subFieldName) => { + const field = this.getFieldByName(subFieldName); + + if (!field) { + // This condition should never happen + return acc; + } + + const runtimeField: EnhancedRuntimeField = { + type: field.type as RuntimeType, + customLabel: field.customLabel, + popularity: field.count, + parent: name, + format: this.getFormatterForFieldNoDefault(field.name)?.toJSON(), + }; + + return { + ...acc, + [subFieldName]: runtimeField, + }; + }, {} as Record); + + return { + ...existingRuntimeComposite, + subFields, + }; + } + + /** + * Remove a runtime composite with its associated subFields + * @param name - Runtime composite name to remove + */ + removeRuntimeComposite(name: string) { + const existingRuntimeComposite = this.getRuntimeComposite(name); + + if (!!existingRuntimeComposite) { + // Remove all previous subFields + for (const subFieldName of existingRuntimeComposite.subFields) { + this.removeRuntimeField(`${name}.${subFieldName}`); + } + + delete this.runtimeCompositeMap[name]; + } + } + /** * Get formatter for a given field name. Return undefined if none exists * @param field diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 74f11badbb411..d2f22abdf3ba6 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -11,7 +11,7 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { INDEX_PATTERN_SAVED_OBJECT_TYPE, SavedObjectsClientCommon } from '../..'; import { createIndexPatternCache } from '.'; -import type { RuntimeField } from '../types'; +import type { RuntimeField, RuntimeComposite } from '../types'; import { IndexPattern } from './index_pattern'; import { createEnsureDefaultIndexPattern, @@ -380,6 +380,7 @@ export class IndexPatternsService { sourceFilters, fieldFormatMap, runtimeFieldMap, + runtimeCompositeMap, typeMeta, type, fieldAttrs, @@ -395,6 +396,9 @@ export class IndexPatternsService { const parsedRuntimeFieldMap: Record = runtimeFieldMap ? JSON.parse(runtimeFieldMap) : {}; + const parsedRuntimeCompositeMap: Record = runtimeCompositeMap + ? JSON.parse(runtimeCompositeMap) + : {}; return { id, @@ -410,6 +414,7 @@ export class IndexPatternsService { fieldAttrs: parsedFieldAttrs, allowNoIndex, runtimeFieldMap: parsedRuntimeFieldMap, + runtimeCompositeMap: parsedRuntimeCompositeMap, }; }; @@ -491,6 +496,10 @@ export class IndexPatternsService { ? JSON.parse(savedObject.attributes.fieldFormatMap) : {}; + spec.runtimeCompositeMap = savedObject.attributes.runtimeCompositeMap + ? JSON.parse(savedObject.attributes.runtimeCompositeMap) + : {}; + const indexPattern = await this.create(spec, true); indexPattern.resetOriginalSavedObjectBody(); return indexPattern; diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index c326e75aca415..1ce502a32757d 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -19,13 +19,39 @@ import { FieldFormat } from '../../../field_formats/common'; export type FieldFormatMap = Record; export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; + export interface RuntimeField { type: RuntimeType; script?: { source: string; }; + parent?: string; + fields?: Record< + string, + { + type: Omit; + } + >; +} + +export interface EnhancedRuntimeField extends RuntimeField { + format?: SerializedFieldFormat; + customLabel?: string; + popularity?: number; } +export interface RuntimeComposite { + name: string; + script: { + source: string; + }; + subFields: string[]; +} + +export type RuntimeCompositeWithSubFields = Omit & { + subFields: Record; +}; + /** * @deprecated * IIndexPattern allows for an IndexPattern OR an index pattern saved object @@ -63,6 +89,7 @@ export interface IndexPatternAttributes { fieldFormatMap?: string; fieldAttrs?: string; runtimeFieldMap?: string; + runtimeCompositeMap?: string; /** * prevents errors when index pattern exists before indices */ @@ -206,6 +233,7 @@ export interface FieldSpec extends IndexPatternFieldBase { // not persisted shortDotsEnable?: boolean; isMapped?: boolean; + parent?: string; } export type IndexPatternFieldMap = Record; @@ -236,6 +264,7 @@ export interface IndexPatternSpec { type?: string; fieldFormats?: Record; runtimeFieldMap?: Record; + runtimeCompositeMap?: Record; fieldAttrs?: FieldAttrs; allowNoIndex?: boolean; } diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 0602f51889a6c..2ae0831ec8c28 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -80,6 +80,7 @@ export { AggregationRestrictions, IndexPatternType, IndexPatternListItem, + RuntimeType, } from '../common'; export { DuplicateIndexPatternError } from '../common/index_patterns/errors'; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index d5a39e3108325..d2178550a9100 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1167,7 +1167,10 @@ export const INDEX_PATTERN_SAVED_OBJECT_TYPE = "index-pattern"; export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); - addRuntimeField(name: string, runtimeField: RuntimeField): void; + // Warning: (ae-forgotten-export) The symbol "RuntimeCompositeWithSubFields" needs to be exported by the entry point index.d.ts + addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; + // Warning: (ae-forgotten-export) The symbol "EnhancedRuntimeField" needs to be exported by the entry point index.d.ts + addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; // @deprecated addScriptedField(name: string, script: string, fieldType?: string): Promise; readonly allowNoIndex: boolean; @@ -1206,7 +1209,9 @@ export class IndexPattern implements IIndexPattern { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; // (undocumented) getFieldAttrs: () => { @@ -1229,6 +1234,9 @@ export class IndexPattern implements IIndexPattern { typeMeta?: string | undefined; type?: string | undefined; }; + // Warning: (ae-forgotten-export) The symbol "RuntimeComposite" needs to be exported by the entry point index.d.ts + getRuntimeComposite(name: string): RuntimeComposite | null; + getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; getRuntimeField(name: string): RuntimeField | null; // @deprecated (undocumented) getScriptedFields(): IndexPatternField[]; @@ -1248,6 +1256,7 @@ export class IndexPattern implements IIndexPattern { isTimeNanosBased(): boolean; // (undocumented) metaFields: string[]; + removeRuntimeComposite(name: string): void; removeRuntimeField(name: string): void; // @deprecated removeScriptedField(fieldName: string): void; @@ -1289,6 +1298,8 @@ export interface IndexPatternAttributes { // (undocumented) intervalName?: string; // (undocumented) + runtimeCompositeMap?: string; + // (undocumented) runtimeFieldMap?: string; // (undocumented) sourceFilters?: string; @@ -1440,6 +1451,8 @@ export interface IndexPatternSpec { // @deprecated (undocumented) intervalName?: string; // (undocumented) + runtimeCompositeMap?: Record; + // (undocumented) runtimeFieldMap?: Record; // (undocumented) sourceFilters?: SourceFilter[]; @@ -1960,6 +1973,12 @@ export type RefreshInterval = { value: number; }; +// Warning: (ae-forgotten-export) The symbol "RUNTIME_FIELD_TYPES" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "RuntimeType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; + // Warning: (ae-missing-release-tag) "SavedQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2340,10 +2359,10 @@ export interface WaitUntilNextSessionCompletesOptions { // Warnings were encountered during analysis: // -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:66:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:139:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:170:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:59:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:73:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:148:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:172:20 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:128:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/search_source/fetch/get_search_params.ts:35:19 - (ae-forgotten-export) The symbol "GetConfigFn" needs to be exported by the entry point index.d.ts // src/plugins/data/public/deprecated.ts:98:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts @@ -2354,20 +2373,20 @@ export interface WaitUntilNextSessionCompletesOptions { // src/plugins/data/public/index.ts:53:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:53:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:53:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:211:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:211:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:211:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:213:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:214:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:223:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:224:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:225:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:226:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:230:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:231:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:235:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:238:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:212:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:212:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:212:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:214:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:215:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:224:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:225:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:226:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:227:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:231:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:232:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:235:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:239:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:62:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts index 7049903f84e8c..06dd09a641ede 100644 --- a/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts +++ b/src/plugins/data/server/index_patterns/routes/create_index_pattern.ts @@ -12,6 +12,7 @@ import { handleErrors } from './util/handle_errors'; import { fieldSpecSchema, runtimeFieldSpecSchema, + runtimeCompositeSpecSchema, serializedFieldFormatSchema, } from './util/schemas'; import { IRouter, StartServicesAccessor } from '../../../../../core/server'; @@ -44,6 +45,7 @@ const indexPatternSpecSchema = schema.object({ ), allowNoIndex: schema.maybe(schema.boolean()), runtimeFieldMap: schema.maybe(schema.recordOf(schema.string(), runtimeFieldSpecSchema)), + runtimeCompositeMap: schema.maybe(schema.recordOf(schema.string(), runtimeCompositeSpecSchema)), }); export const registerCreateIndexPatternRoute = ( diff --git a/src/plugins/data/server/index_patterns/routes/util/schemas.ts b/src/plugins/data/server/index_patterns/routes/util/schemas.ts index 79ee1ffa1ab97..7fc1030e4eaee 100644 --- a/src/plugins/data/server/index_patterns/routes/util/schemas.ts +++ b/src/plugins/data/server/index_patterns/routes/util/schemas.ts @@ -74,3 +74,10 @@ export const runtimeFieldSpec = { ), }; export const runtimeFieldSpecSchema = schema.object(runtimeFieldSpec); + +export const runtimeCompositeSpec = { + name: schema.string(), + script: runtimeFieldSpec.script, + fields: schema.arrayOf(schema.string()), +}; +export const runtimeCompositeSpecSchema = schema.object(runtimeCompositeSpec); diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index f994db960669f..503d19771c6ab 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -269,7 +269,10 @@ export const INDEX_PATTERN_SAVED_OBJECT_TYPE = "index-pattern"; export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); - addRuntimeField(name: string, runtimeField: RuntimeField): void; + // Warning: (ae-forgotten-export) The symbol "RuntimeCompositeWithSubFields" needs to be exported by the entry point index.d.ts + addRuntimeComposite(name: string, runtimeComposite: RuntimeCompositeWithSubFields): IndexPatternField[]; + // Warning: (ae-forgotten-export) The symbol "EnhancedRuntimeField" needs to be exported by the entry point index.d.ts + addRuntimeField(name: string, enhancedRuntimeField: EnhancedRuntimeField): IndexPatternField; // @deprecated addScriptedField(name: string, script: string, fieldType?: string): Promise; readonly allowNoIndex: boolean; @@ -310,7 +313,9 @@ export class IndexPattern implements IIndexPattern { field: any; format: string; }[]; - runtimeFields: Record; + runtimeFields: { + [x: string]: RuntimeField; + }; }; // (undocumented) getFieldAttrs: () => { @@ -334,6 +339,9 @@ export class IndexPattern implements IIndexPattern { typeMeta?: string | undefined; type?: string | undefined; }; + // Warning: (ae-forgotten-export) The symbol "RuntimeComposite" needs to be exported by the entry point index.d.ts + getRuntimeComposite(name: string): RuntimeComposite | null; + getRuntimeCompositeWithSubFields(name: string): RuntimeCompositeWithSubFields | null; getRuntimeField(name: string): RuntimeField | null; // @deprecated (undocumented) getScriptedFields(): IndexPatternField[]; @@ -353,6 +361,7 @@ export class IndexPattern implements IIndexPattern { isTimeNanosBased(): boolean; // (undocumented) metaFields: string[]; + removeRuntimeComposite(name: string): void; removeRuntimeField(name: string): void; // @deprecated removeScriptedField(fieldName: string): void; @@ -398,6 +407,8 @@ export interface IndexPatternAttributes { // (undocumented) intervalName?: string; // (undocumented) + runtimeCompositeMap?: string; + // (undocumented) runtimeFieldMap?: string; // (undocumented) sourceFilters?: string; @@ -861,10 +872,10 @@ export const UI_SETTINGS: { // Warnings were encountered during analysis: // -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:66:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:139:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:170:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:59:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:73:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:148:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:172:20 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:21:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:97:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:98:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx index 4a4c42f69fc8e..16803533bf7fd 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor.test.tsx @@ -98,7 +98,7 @@ describe('', () => { test('should accept a defaultValue and onChange prop to forward the form state', async () => { const field = { name: 'foo', - type: 'date', + type: 'date' as const, script: { source: 'emit("hello")' }, }; diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts index 9b00ff762fe8f..35ffb7f58b6c7 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_content.test.ts @@ -33,7 +33,7 @@ describe('', () => { test('should allow a field to be provided', async () => { const field = { name: 'foo', - type: 'ip', + type: 'ip' as const, script: { source: 'emit("hello world")', }, @@ -50,7 +50,7 @@ describe('', () => { test('should accept an "onSave" prop', async () => { const field = { name: 'foo', - type: 'date', + type: 'date' as const, script: { source: 'test=123' }, }; const onSave: jest.Mock = jest.fn(); diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts index 65089bc24317b..33e74aec1bb83 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts @@ -280,7 +280,7 @@ describe('Field editor Preview panel', () => { test('should **not** display an empty prompt editing a document with a script', async () => { const field = { name: 'foo', - type: 'ip', + type: 'ip' as const, script: { source: 'emit("hello world")', }, @@ -303,7 +303,7 @@ describe('Field editor Preview panel', () => { test('should **not** display an empty prompt editing a document with format defined', async () => { const field = { name: 'foo', - type: 'ip', + type: 'ip' as const, format: { id: 'upper', params: {}, diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts index e262d3ecbfe45..30f3c20131ae7 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts @@ -38,4 +38,8 @@ export const RUNTIME_FIELD_OPTIONS: Array> label: 'Geo point', value: 'geo_point', }, + { + label: 'Composite', + value: 'composite', + }, ]; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx index 19015aa9d0d10..20ccd59ae878f 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx @@ -20,9 +20,10 @@ import { EuiText, } from '@elastic/eui'; -import type { Field, EsRuntimeField } from '../types'; +import type { Field, CompositeField } from '../types'; import { RuntimeFieldPainlessError } from '../lib'; import { euiFlyoutClassname } from '../constants'; +import type { RuntimeField, EnhancedRuntimeField } from '../shared_imports'; import { FlyoutPanels } from './flyout_panels'; import { useFieldEditorContext } from './field_editor_context'; import { FieldEditor, FieldEditorFormState } from './field_editor/field_editor'; @@ -50,13 +51,13 @@ export interface Props { /** * Handler for the "save" footer button */ - onSave: (field: Field) => void; + onSave: (field: Field | CompositeField) => void; /** * Handler for the "cancel" footer button */ onCancel: () => void; /** Handler to validate the script */ - runtimeFieldValidator: (field: EsRuntimeField) => Promise; + runtimeFieldValidator: (field: RuntimeField) => Promise; /** Optional field to process */ field?: Field; isSavingField: boolean; @@ -78,6 +79,7 @@ const FieldEditorFlyoutContentComponent = ({ const { indexPattern } = useFieldEditorContext(); const { panel: { isVisible: isPanelVisible }, + fieldsInScript, } = useFieldPreviewContext(); const [formState, setFormState] = useState({ @@ -119,18 +121,48 @@ const FieldEditorFlyoutContentComponent = ({ return !isFormModified; }, [isFormModified]); + const addSubfieldsToField = useCallback( + (_field: Field): Field | CompositeField => { + if (_field.type === 'composite' && fieldsInScript.length > 0) { + const subFields = fieldsInScript.reduce((acc, subFieldName) => { + const subField: EnhancedRuntimeField = { + // We hardcode "keyword" and "format" for now until the UI + // lets us define them for each of the sub fields + type: 'keyword' as const, + format: _field.format, + }; + + acc[subFieldName] = subField; + return acc; + }, {} as Record); + + const updatedField: CompositeField = { + type: 'composite', + name: _field.name, + script: _field.script, + subFields, + }; + + return updatedField; + } + + return _field; + }, + [fieldsInScript] + ); + const onClickSave = useCallback(async () => { - const { isValid, data } = await submit(); - const nameChange = field?.name !== data.name; - const typeChange = field?.type !== data.type; + const { isValid, data: updatedField } = await submit(); + const nameChange = field?.name !== updatedField.name; + const typeChange = field?.type !== updatedField.type; if (isValid) { - if (data.script) { + if (updatedField.script) { setIsValidating(true); const error = await runtimeFieldValidator({ - type: data.type, - script: data.script, + type: updatedField.type, + script: updatedField.script, }); setIsValidating(false); @@ -147,10 +179,10 @@ const FieldEditorFlyoutContentComponent = ({ confirmChangeNameOrType: true, }); } else { - onSave(data); + onSave(addSubfieldsToField(updatedField)); } } - }, [onSave, submit, runtimeFieldValidator, field, isEditingExistingField]); + }, [onSave, submit, runtimeFieldValidator, field, isEditingExistingField, addSubfieldsToField]); const onClickCancel = useCallback(() => { const canClose = canCloseValidator(); @@ -166,8 +198,8 @@ const FieldEditorFlyoutContentComponent = ({ { - const { data } = await submit(); - onSave(data); + const { data: updatedField } = await submit(); + onSave(addSubfieldsToField(updatedField)); }} onCancel={() => { setModalVisibility(defaultModalVisibility); diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx index cf2b29bbc97e8..9a00f0fb62b0d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -15,10 +15,9 @@ import { IndexPatternField, IndexPattern, DataPublicPluginStart, - RuntimeType, UsageCollectionStart, } from '../shared_imports'; -import type { Field, PluginStart, InternalFieldType } from '../types'; +import type { Field, PluginStart, InternalFieldType, CompositeField } from '../types'; import { pluginName } from '../constants'; import { deserializeField, getRuntimeFieldValidator, getLinks, ApiService } from '../lib'; import { @@ -30,7 +29,7 @@ import { FieldPreviewProvider } from './preview'; export interface Props { /** Handler for the "save" footer button */ - onSave: (field: IndexPatternField) => void; + onSave: (field: IndexPatternField[]) => void; /** Handler for the "cancel" footer button */ onCancel: () => void; onMounted?: FieldEditorFlyoutContentProps['onMounted']; @@ -117,26 +116,52 @@ export const FieldEditorFlyoutContentContainer = ({ [apiService, search, notifications] ); + const saveCompositeRuntime = useCallback( + (updatedField: CompositeField): IndexPatternField[] => { + if (field?.type !== undefined && field?.type !== 'composite') { + // A previous runtime field is now a runtime composite + indexPattern.removeRuntimeField(field.name); + } else if (field?.name && field.name !== updatedField.name) { + // rename an existing runtime composite + indexPattern.removeRuntimeComposite(field.name); + } + + const { name, script, subFields } = updatedField; + + return indexPattern.addRuntimeComposite(name, { + name, + script: script!, + subFields, + }); + }, + [field?.name, field?.type, indexPattern] + ); + + const saveRuntimeField = useCallback( + (updatedField: Field): [IndexPatternField] => { + if (field?.type !== undefined && field?.type === 'object') { + // A previous runtime object is now a runtime field + indexPattern.removeRuntimeComposite(field.name); + } else if (field?.name && field.name !== updatedField.name) { + // rename an existing runtime field + indexPattern.removeRuntimeField(field.name); + } + + const fieldCreated = indexPattern.addRuntimeField(updatedField.name, updatedField); + return [fieldCreated]; + }, + [field?.name, field?.type, indexPattern] + ); + const saveField = useCallback( - async (updatedField: Field) => { + async (updatedField: Field | CompositeField) => { setIsSaving(true); - const { script } = updatedField; - if (fieldTypeToProcess === 'runtime') { try { usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); // eslint-disable-next-line no-empty } catch {} - // rename an existing runtime field - if (field?.name && field.name !== updatedField.name) { - indexPattern.removeRuntimeField(field.name); - } - - indexPattern.addRuntimeField(updatedField.name, { - type: updatedField.type as RuntimeType, - script, - }); } else { try { usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_concrete'); @@ -144,32 +169,22 @@ export const FieldEditorFlyoutContentContainer = ({ } catch {} } - const editedField = indexPattern.getFieldByName(updatedField.name); - try { - if (!editedField) { - throw new Error( - `Unable to find field named '${updatedField.name}' on index pattern '${indexPattern.title}'` - ); - } - - indexPattern.setFieldCustomLabel(updatedField.name, updatedField.customLabel); - editedField.count = updatedField.popularity || 0; - if (updatedField.format) { - indexPattern.setFieldFormat(updatedField.name, updatedField.format); - } else { - indexPattern.deleteFieldFormat(updatedField.name); - } - - await indexPatternService.updateSavedObject(indexPattern).then(() => { - const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { - defaultMessage: "Saved '{fieldName}'", - values: { fieldName: updatedField.name }, - }); - notifications.toasts.addSuccess(message); - setIsSaving(false); - onSave(editedField); + const editedFields: IndexPatternField[] = + updatedField.type === 'composite' + ? saveCompositeRuntime(updatedField as CompositeField) + : saveRuntimeField(updatedField); + + await indexPatternService.updateSavedObject(indexPattern); + + const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { + defaultMessage: "Saved '{fieldName}'", + values: { fieldName: updatedField.name }, }); + notifications.toasts.addSuccess(message); + + setIsSaving(false); + onSave(editedFields); } catch (e) { const title = i18n.translate('indexPatternFieldEditor.save.errorTitle', { defaultMessage: 'Failed to save field changes', @@ -184,8 +199,9 @@ export const FieldEditorFlyoutContentContainer = ({ indexPatternService, notifications, fieldTypeToProcess, - field?.name, usageCollection, + saveRuntimeField, + saveCompositeRuntime, ] ); diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx index 09bacf2a46096..7fc8dae1f025c 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview.tsx @@ -27,6 +27,7 @@ export const FieldPreview = () => { params: { value: { name, script, format }, }, + isLoadingPreview, fields, error, reset, @@ -34,15 +35,15 @@ export const FieldPreview = () => { // To show the preview we at least need a name to be defined, the script or the format // and an first response from the _execute API - const isEmptyPromptVisible = - name === null && script === null && format === null - ? true - : // If we have some result from the _execute API call don't show the empty prompt - error !== null || fields.length > 0 - ? false - : name === null && format === null - ? true - : false; + let isEmptyPromptVisible = false; + const noParamDefined = name === null && script === null && format === null; + const haveResultFromPreview = error !== null || fields.length > 0; + + if (noParamDefined) { + isEmptyPromptVisible = true; + } else if (!haveResultFromPreview && !isLoadingPreview && name === null && format === null) { + isEmptyPromptVisible = true; + } const onFieldListResize = useCallback(({ height }: { height: number }) => { setFieldListHeight(height); @@ -53,13 +54,13 @@ export const FieldPreview = () => { return null; } - const [field] = fields; - return (
    -
  • - -
  • + {fields.map((field, i) => ( +
  • + +
  • + ))}
); }; diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx index e1fc4b05883f4..2c74f4899f159 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx @@ -94,6 +94,8 @@ interface Context { value: { [key: string]: boolean }; set: React.Dispatch>; }; + /** List of fields detected in the Painless script */ + fieldsInScript: string[]; } const fieldPreviewContext = createContext(undefined); @@ -157,6 +159,8 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { const [from, setFrom] = useState('cluster'); /** Map of fields pinned to the top of the list */ const [pinnedFields, setPinnedFields] = useState<{ [key: string]: boolean }>({}); + /** Array of fields detected in the script (returned by the _execute API) */ + const [fieldsInScript, setFieldsInScript] = useState([]); const { documents, currentIdx } = clusterData; const currentDocument: EsDocument | undefined = useMemo(() => documents[currentIdx], [ @@ -317,6 +321,53 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { [indexPattern, search] ); + const updateSingleFieldPreview = useCallback( + (fieldName: string, values: unknown[]) => { + const [value] = values; + const formattedValue = valueFormatter(value); + + setPreviewResponse({ + fields: [{ key: fieldName, value, formattedValue }], + error: null, + }); + }, + [valueFormatter] + ); + + const updateCompositeFieldPreview = useCallback( + (compositeName: string | null, compositeValues: Record) => { + if (typeof compositeValues !== 'object') { + return; + } + + const updatedFieldsInScript: string[] = []; + + const fields = Object.entries(compositeValues).map(([key, values]) => { + // The Painless _execute API returns the composite field values under a map. + // Each of the key is prefixed with "composite_field." (e.g. "composite_field.field1: ['value']") + const { 1: fieldName } = key.split('composite_field.'); + updatedFieldsInScript.push(fieldName); + + const [value] = values; + const formattedValue = valueFormatter(value); + + return { + key: `${compositeName ?? ''}.${fieldName}`, + value, + formattedValue, + }; + }); + + setPreviewResponse({ + fields, + error: null, + }); + + setFieldsInScript(updatedFieldsInScript); + }, + [valueFormatter] + ); + const updatePreview = useCallback(async () => { setLastExecutePainlessReqParams({ type: params.type, @@ -371,13 +422,11 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { error: { code: 'PAINLESS_SCRIPT_ERROR', error: parseEsError(error, true) ?? fallBackError }, }); } else { - const [value] = values; - const formattedValue = valueFormatter(value); - - setPreviewResponse({ - fields: [{ key: params.name!, value, formattedValue }], - error: null, - }); + if (!Array.isArray(values)) { + updateCompositeFieldPreview(params.name, values); + return; + } + updateSingleFieldPreview(params.name!, values); } }, [ needToUpdatePreview, @@ -386,7 +435,8 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { currentDocId, getFieldPreview, notifications.toasts, - valueFormatter, + updateSingleFieldPreview, + updateCompositeFieldPreview, ]); const goToNextDoc = useCallback(() => { @@ -463,6 +513,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { value: pinnedFields, set: setPinnedFields, }, + fieldsInScript, }), [ previewResponse, @@ -482,6 +533,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { from, reset, pinnedFields, + fieldsInScript, ] ); @@ -543,24 +595,64 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { }, [currentDocument, updateParams]); /** - * Whenever the name or the format changes we immediately update the preview + * Whenever the name changes we immediately update the preview */ useEffect(() => { setPreviewResponse((prev) => { - const { - fields: { 0: field }, - } = prev; + const { fields } = prev; + + let updatedFields: Context['fields'] = fields.map((field) => { + let key: string = name ?? ''; + + if (type === 'composite') { + const { 1: fieldName } = field.key.split('.'); + key = `${name ?? ''}.${fieldName}`; + } + + return { + ...field, + key, + }; + }); - const nextValue = - script === null && Boolean(document) - ? get(document, name ?? '') // When there is no script we read the value from _source - : field?.value; + // If the user has entered a name but not yet any script we will display + // the field in the preview with just the name (and a "-" for the value) + if (updatedFields.length === 0 && name !== null) { + updatedFields = [ + { key: name, value: undefined, formattedValue: defaultValueFormatter(undefined) }, + ]; + } - const formattedValue = valueFormatter(nextValue); + return { + ...prev, + fields: updatedFields, + }; + }); + }, [name, type]); + + /** + * Whenever the format changes we immediately update the preview + */ + useEffect(() => { + setPreviewResponse((prev) => { + const { fields } = prev; return { ...prev, - fields: [{ ...field, key: name ?? '', value: nextValue, formattedValue }], + fields: fields.map((field) => { + const nextValue = + script === null && Boolean(document) + ? get(document, name ?? '') // When there is no script we try to read the value from _source + : field?.value; + + const formattedValue = valueFormatter(nextValue); + + return { + ...field, + value: nextValue, + formattedValue, + }; + }), }; }); }, [name, script, document, valueFormatter]); diff --git a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts index 789c4f7fa71fc..5553e8d583417 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts @@ -7,8 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { DataPublicPluginStart } from '../shared_imports'; -import type { EsRuntimeField } from '../types'; +import { DataPublicPluginStart, RuntimeField } from '../shared_imports'; export interface RuntimeFieldPainlessError { message: string; @@ -95,7 +94,7 @@ export const parseEsError = ( export const getRuntimeFieldValidator = ( index: string, searchService: DataPublicPluginStart['search'] -) => async (runtimeField: EsRuntimeField) => { +) => async (runtimeField: RuntimeField) => { return await searchService .search({ params: { diff --git a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts index 8a0a47e07c9c9..8400138d12d39 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/serialization.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/serialization.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IndexPatternField, IndexPattern } from '../shared_imports'; +import { IndexPatternField, IndexPattern, RuntimeType } from '../shared_imports'; import type { Field } from '../types'; export const deserializeField = ( @@ -19,7 +19,7 @@ export const deserializeField = ( return { name: field.name, - type: field?.esTypes ? field.esTypes[0] : 'keyword', + type: (field?.esTypes ? field.esTypes[0] : 'keyword') as RuntimeType, script: field.runtimeField ? field.runtimeField.script : undefined, customLabel: field.customLabel, popularity: field.count, diff --git a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx index 72dbb76863353..578ed785df86c 100644 --- a/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_delete_modal.tsx @@ -40,6 +40,20 @@ export const getFieldDeleteModalOpener = ({ indexPatternService, usageCollection, }: Dependencies) => (options: OpenFieldDeleteModalOptions): CloseEditor => { + if (typeof options.fieldName === 'string') { + const fieldToDelete = options.ctx.indexPattern.getFieldByName(options.fieldName); + const parent = fieldToDelete?.runtimeField?.parent; + const doesBelongToCompositeField = + parent === undefined ? false : options.ctx.indexPattern.getRuntimeComposite(parent) !== null; + + if (doesBelongToCompositeField) { + console.log( // eslint-disable-line + 'TODO: display a modal to indicate that this field needs to be deleted through its parent.' + ); + return () => undefined; + } + } + const { overlays, notifications } = core; let overlayRef: OverlayRef | null = null; diff --git a/src/plugins/index_pattern_field_editor/public/open_editor.tsx b/src/plugins/index_pattern_field_editor/public/open_editor.tsx index 946e666bf8205..c72325ba25cc7 100644 --- a/src/plugins/index_pattern_field_editor/public/open_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/open_editor.tsx @@ -28,7 +28,7 @@ export interface OpenFieldEditorOptions { ctx: { indexPattern: IndexPattern; }; - onSave?: (field: IndexPatternField) => void; + onSave?: (field: IndexPatternField[]) => void; fieldName?: string; } @@ -80,7 +80,7 @@ export const getFieldEditorOpener = ({ } }; - const onSaveField = (updatedField: IndexPatternField) => { + const onSaveField = (updatedField: IndexPatternField[]) => { closeEditor(); if (onSave) { @@ -104,6 +104,13 @@ export const getFieldEditorOpener = ({ const fieldTypeToProcess: InternalFieldType = isNewRuntimeField || isExistingRuntimeField ? 'runtime' : 'concrete'; + if (field?.runtimeField?.parent !== undefined) { + console.log( // eslint-disable-line + 'TODO: display a modal to indicate that this field needs to be edited through its parent.' + ); + return closeEditor; + } + overlayRef = overlays.openFlyout( toMountPoint( diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index 2827928d1c060..afcd70ef50378 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -10,7 +10,13 @@ export { IndexPattern, IndexPatternField, DataPublicPluginStart } from '../../da export { UsageCollectionStart } from '../../usage_collection/public'; -export { RuntimeType, RuntimeField, KBN_FIELD_TYPES, ES_FIELD_TYPES } from '../../data/common'; +export { + RuntimeType, + RuntimeField, + KBN_FIELD_TYPES, + ES_FIELD_TYPES, + EnhancedRuntimeField, +} from '../../data/common'; export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; diff --git a/src/plugins/index_pattern_field_editor/public/types.ts b/src/plugins/index_pattern_field_editor/public/types.ts index f7efc9d82fc48..3913cb183ba0f 100644 --- a/src/plugins/index_pattern_field_editor/public/types.ts +++ b/src/plugins/index_pattern_field_editor/public/types.ts @@ -11,8 +11,8 @@ import { FunctionComponent } from 'react'; import { DataPublicPluginStart, RuntimeField, - RuntimeType, UsageCollectionStart, + EnhancedRuntimeField, } from './shared_imports'; import { OpenFieldEditorOptions } from './open_editor'; import { OpenFieldDeleteModalOptions } from './open_delete_modal'; @@ -43,26 +43,17 @@ export interface StartPlugins { export type InternalFieldType = 'concrete' | 'runtime'; -export interface Field { +export interface Field extends EnhancedRuntimeField { name: string; - type: RuntimeField['type'] | string; - script?: RuntimeField['script']; - customLabel?: string; - popularity?: number; - format?: FieldFormatConfig; } -export interface FieldFormatConfig { - id: string; - params?: { [key: string]: any }; +export interface CompositeField extends RuntimeField { + type: 'composite'; + name: string; + subFields: Record; } -export interface EsRuntimeField { - type: RuntimeType | string; - script?: { - source: string; - }; -} +export type FieldFormatConfig = EnhancedRuntimeField['format']; export type CloseEditor = () => void; diff --git a/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts b/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts index 238701904e22c..ff66d711adf96 100644 --- a/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts +++ b/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts @@ -24,6 +24,7 @@ const bodySchema = schema.object({ schema.literal('ip_field'), schema.literal('keyword_field'), schema.literal('long_field'), + schema.literal('composite_field'), ]), document: schema.object({}, { unknowns: 'allow' }), }); diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index a64594e86a757..8690720814e96 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -6,7 +6,6 @@ */ import moment from 'moment-timezone'; -import { estypes } from '@elastic/elasticsearch'; import { useEffect, useMemo } from 'react'; import { @@ -24,6 +23,7 @@ import { IFieldType, ES_FIELD_TYPES, KBN_FIELD_TYPES, + RuntimeType, } from '../../../../../../../src/plugins/data/public'; import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; @@ -181,7 +181,7 @@ export const getDataGridSchemasFromFieldTypes = (fieldTypes: FieldTypes, results export const NON_AGGREGATABLE = 'non-aggregatable'; export const getDataGridSchemaFromESFieldType = ( - fieldType: ES_FIELD_TYPES | undefined | estypes.MappingRuntimeField['type'] + fieldType: ES_FIELD_TYPES | undefined | RuntimeType ): string | undefined => { // Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json'] // To fall back to the default string schema it needs to be undefined. diff --git a/x-pack/plugins/transform/common/api_schemas/common.ts b/x-pack/plugins/transform/common/api_schemas/common.ts index 5354598dc2475..fdefa3a8c4ad9 100644 --- a/x-pack/plugins/transform/common/api_schemas/common.ts +++ b/x-pack/plugins/transform/common/api_schemas/common.ts @@ -68,6 +68,7 @@ export const runtimeMappingsSchema = schema.maybe( schema.literal('ip'), schema.literal('boolean'), schema.literal('geo_point'), + schema.literal('composite'), ]), script: schema.maybe( schema.oneOf([