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([