[Discover] Allow custom name for fields via index pattern field management (#70039)

Co-authored-by: Matt Kime <matt@mattki.me>
This commit is contained in:
Matthias Wilhelm 2020-11-17 13:18:07 +01:00 committed by GitHub
parent de8931546d
commit 0a7f462939
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 690 additions and 188 deletions

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) &gt; [customName](./kibana-plugin-plugins-data-public.ifieldtype.customname.md)
## IFieldType.customName property
<b>Signature:</b>
```typescript
customName?: string;
```

View file

@ -16,6 +16,7 @@ export interface IFieldType
| --- | --- | --- |
| [aggregatable](./kibana-plugin-plugins-data-public.ifieldtype.aggregatable.md) | <code>boolean</code> | |
| [count](./kibana-plugin-plugins-data-public.ifieldtype.count.md) | <code>number</code> | |
| [customName](./kibana-plugin-plugins-data-public.ifieldtype.customname.md) | <code>string</code> | |
| [displayName](./kibana-plugin-plugins-data-public.ifieldtype.displayname.md) | <code>string</code> | |
| [esTypes](./kibana-plugin-plugins-data-public.ifieldtype.estypes.md) | <code>string[]</code> | |
| [filterable](./kibana-plugin-plugins-data-public.ifieldtype.filterable.md) | <code>boolean</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [fieldAttrs](./kibana-plugin-plugins-data-public.indexpattern.fieldattrs.md)
## IndexPattern.fieldAttrs property
<b>Signature:</b>
```typescript
fieldAttrs: FieldAttrs;
```

View file

@ -10,6 +10,7 @@ Returns index pattern as saved object body for saving
```typescript
getAsSavedObjectBody(): {
fieldAttrs: string | undefined;
title: string;
timeFieldName: string | undefined;
intervalName: string | undefined;
@ -23,6 +24,7 @@ getAsSavedObjectBody(): {
<b>Returns:</b>
`{
fieldAttrs: string | undefined;
title: string;
timeFieldName: string | undefined;
intervalName: string | undefined;

View file

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) &gt; [getFieldAttrs](./kibana-plugin-plugins-data-public.indexpattern.getfieldattrs.md)
## IndexPattern.getFieldAttrs property
<b>Signature:</b>
```typescript
getFieldAttrs: () => {
[x: string]: {
customName: string;
};
};
```

View file

@ -10,6 +10,7 @@ Get last saved saved object fields
```typescript
getOriginalSavedObjectBody: () => {
fieldAttrs?: string | undefined;
title?: string | undefined;
timeFieldName?: string | undefined;
intervalName?: string | undefined;

View file

@ -21,12 +21,14 @@ export declare class IndexPattern implements IIndexPattern
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [deleteFieldFormat](./kibana-plugin-plugins-data-public.indexpattern.deletefieldformat.md) | | <code>(fieldName: string) =&gt; void</code> | |
| [fieldAttrs](./kibana-plugin-plugins-data-public.indexpattern.fieldattrs.md) | | <code>FieldAttrs</code> | |
| [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md) | | <code>Record&lt;string, any&gt;</code> | |
| [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | <code>IIndexPatternFieldList &amp; {</code><br/><code> toSpec: () =&gt; IndexPatternFieldMap;</code><br/><code> }</code> | |
| [flattenHit](./kibana-plugin-plugins-data-public.indexpattern.flattenhit.md) | | <code>(hit: Record&lt;string, any&gt;, deep?: boolean) =&gt; Record&lt;string, any&gt;</code> | |
| [formatField](./kibana-plugin-plugins-data-public.indexpattern.formatfield.md) | | <code>FormatFieldFn</code> | |
| [formatHit](./kibana-plugin-plugins-data-public.indexpattern.formathit.md) | | <code>{</code><br/><code> (hit: Record&lt;string, any&gt;, type?: string): any;</code><br/><code> formatField: FormatFieldFn;</code><br/><code> }</code> | |
| [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.getoriginalsavedobjectbody.md) | | <code>() =&gt; {</code><br/><code> title?: string &#124; undefined;</code><br/><code> timeFieldName?: string &#124; undefined;</code><br/><code> intervalName?: string &#124; undefined;</code><br/><code> fields?: string &#124; undefined;</code><br/><code> sourceFilters?: string &#124; undefined;</code><br/><code> fieldFormatMap?: string &#124; undefined;</code><br/><code> typeMeta?: string &#124; undefined;</code><br/><code> type?: string &#124; undefined;</code><br/><code> }</code> | Get last saved saved object fields |
| [getFieldAttrs](./kibana-plugin-plugins-data-public.indexpattern.getfieldattrs.md) | | <code>() =&gt; {</code><br/><code> [x: string]: {</code><br/><code> customName: string;</code><br/><code> };</code><br/><code> }</code> | |
| [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.getoriginalsavedobjectbody.md) | | <code>() =&gt; {</code><br/><code> fieldAttrs?: string &#124; undefined;</code><br/><code> title?: string &#124; undefined;</code><br/><code> timeFieldName?: string &#124; undefined;</code><br/><code> intervalName?: string &#124; undefined;</code><br/><code> fields?: string &#124; undefined;</code><br/><code> sourceFilters?: string &#124; undefined;</code><br/><code> fieldFormatMap?: string &#124; undefined;</code><br/><code> typeMeta?: string &#124; undefined;</code><br/><code> type?: string &#124; undefined;</code><br/><code> }</code> | Get last saved saved object fields |
| [id](./kibana-plugin-plugins-data-public.indexpattern.id.md) | | <code>string</code> | |
| [intervalName](./kibana-plugin-plugins-data-public.indexpattern.intervalname.md) | | <code>string &#124; undefined</code> | |
| [metaFields](./kibana-plugin-plugins-data-public.indexpattern.metafields.md) | | <code>string[]</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) &gt; [fieldAttrs](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldattrs.md)
## IndexPatternAttributes.fieldAttrs property
<b>Signature:</b>
```typescript
fieldAttrs?: string;
```

View file

@ -14,6 +14,7 @@ export interface IndexPatternAttributes
| Property | Type | Description |
| --- | --- | --- |
| [fieldAttrs](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldattrs.md) | <code>string</code> | |
| [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldformatmap.md) | <code>string</code> | |
| [fields](./kibana-plugin-plugins-data-public.indexpatternattributes.fields.md) | <code>string</code> | |
| [intervalName](./kibana-plugin-plugins-data-public.indexpatternattributes.intervalname.md) | <code>string</code> | |

View file

@ -9,7 +9,7 @@ Constructs a new instance of the `IndexPatternField` class
<b>Signature:</b>
```typescript
constructor(spec: FieldSpec, displayName: string);
constructor(spec: FieldSpec);
```
## Parameters
@ -17,5 +17,4 @@ constructor(spec: FieldSpec, displayName: string);
| Parameter | Type | Description |
| --- | --- | --- |
| spec | <code>FieldSpec</code> | |
| displayName | <code>string</code> | |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) &gt; [customName](./kibana-plugin-plugins-data-public.indexpatternfield.customname.md)
## IndexPatternField.customName property
<b>Signature:</b>
```typescript
get customName(): string | undefined;
set customName(label: string | undefined);
```

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
readonly displayName: string;
get displayName(): string;
```

View file

@ -14,7 +14,7 @@ export declare class IndexPatternField implements IFieldType
| Constructor | Modifiers | Description |
| --- | --- | --- |
| [(constructor)(spec, displayName)](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) | | Constructs a new instance of the <code>IndexPatternField</code> class |
| [(constructor)(spec)](./kibana-plugin-plugins-data-public.indexpatternfield._constructor_.md) | | Constructs a new instance of the <code>IndexPatternField</code> class |
## Properties
@ -23,6 +23,7 @@ export declare class IndexPatternField implements IFieldType
| [aggregatable](./kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md) | | <code>boolean</code> | |
| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | <code>Record&lt;string, string[]&gt; &#124; undefined</code> | Description of field type conflicts across different indices in the same index pattern |
| [count](./kibana-plugin-plugins-data-public.indexpatternfield.count.md) | | <code>number</code> | Count is used for field popularity |
| [customName](./kibana-plugin-plugins-data-public.indexpatternfield.customname.md) | | <code>string &#124; undefined</code> | |
| [displayName](./kibana-plugin-plugins-data-public.indexpatternfield.displayname.md) | | <code>string</code> | |
| [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | <code>string[] &#124; undefined</code> | |
| [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | <code>boolean</code> | |

View file

@ -20,6 +20,7 @@ toJSON(): {
aggregatable: boolean;
readFromDocValues: boolean;
subType: import("../types").IFieldSubType | undefined;
customName: string | undefined;
};
```
<b>Returns:</b>
@ -37,5 +38,6 @@ toJSON(): {
aggregatable: boolean;
readFromDocValues: boolean;
subType: import("../types").IFieldSubType | undefined;
customName: string | undefined;
}`

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) &gt; [fieldAttrs](./kibana-plugin-plugins-data-public.indexpatternspec.fieldattrs.md)
## IndexPatternSpec.fieldAttrs property
<b>Signature:</b>
```typescript
fieldAttrs?: FieldAttrs;
```

View file

@ -14,6 +14,7 @@ export interface IndexPatternSpec
| Property | Type | Description |
| --- | --- | --- |
| [fieldAttrs](./kibana-plugin-plugins-data-public.indexpatternspec.fieldattrs.md) | <code>FieldAttrs</code> | |
| [fieldFormats](./kibana-plugin-plugins-data-public.indexpatternspec.fieldformats.md) | <code>Record&lt;string, SerializedFieldFormat&gt;</code> | |
| [fields](./kibana-plugin-plugins-data-public.indexpatternspec.fields.md) | <code>IndexPatternFieldMap</code> | |
| [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) | <code>string</code> | |

View file

@ -9,5 +9,5 @@ Converts field array to map
<b>Signature:</b>
```typescript
fieldArrayToMap: (fields: FieldSpec[]) => Record<string, FieldSpec>;
fieldArrayToMap: (fields: FieldSpec[], fieldAttrs?: FieldAttrs | undefined) => Record<string, FieldSpec>;
```

View file

@ -22,7 +22,7 @@ export declare class IndexPatternsService
| --- | --- | --- | --- |
| [clearCache](./kibana-plugin-plugins-data-public.indexpatternsservice.clearcache.md) | | <code>(id?: string &#124; undefined) =&gt; void</code> | Clear index pattern list cache |
| [ensureDefaultIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.ensuredefaultindexpattern.md) | | <code>EnsureDefaultIndexPattern</code> | |
| [fieldArrayToMap](./kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md) | | <code>(fields: FieldSpec[]) =&gt; Record&lt;string, FieldSpec&gt;</code> | Converts field array to map |
| [fieldArrayToMap](./kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md) | | <code>(fields: FieldSpec[], fieldAttrs?: FieldAttrs &#124; undefined) =&gt; Record&lt;string, FieldSpec&gt;</code> | Converts field array to map |
| [get](./kibana-plugin-plugins-data-public.indexpatternsservice.get.md) | | <code>(id: string) =&gt; Promise&lt;IndexPattern&gt;</code> | Get an index pattern by id. Cache optimized |
| [getCache](./kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md) | | <code>() =&gt; Promise&lt;SavedObject&lt;IndexPatternSavedObjectAttrs&gt;[] &#124; null &#124; undefined&gt;</code> | |
| [getDefault](./kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md) | | <code>() =&gt; Promise&lt;IndexPattern &#124; null&gt;</code> | Get default index pattern |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) &gt; [customName](./kibana-plugin-plugins-data-server.ifieldtype.customname.md)
## IFieldType.customName property
<b>Signature:</b>
```typescript
customName?: string;
```

View file

@ -16,6 +16,7 @@ export interface IFieldType
| --- | --- | --- |
| [aggregatable](./kibana-plugin-plugins-data-server.ifieldtype.aggregatable.md) | <code>boolean</code> | |
| [count](./kibana-plugin-plugins-data-server.ifieldtype.count.md) | <code>number</code> | |
| [customName](./kibana-plugin-plugins-data-server.ifieldtype.customname.md) | <code>string</code> | |
| [displayName](./kibana-plugin-plugins-data-server.ifieldtype.displayname.md) | <code>string</code> | |
| [esTypes](./kibana-plugin-plugins-data-server.ifieldtype.estypes.md) | <code>string[]</code> | |
| [filterable](./kibana-plugin-plugins-data-server.ifieldtype.filterable.md) | <code>boolean</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) &gt; [fieldAttrs](./kibana-plugin-plugins-data-server.indexpattern.fieldattrs.md)
## IndexPattern.fieldAttrs property
<b>Signature:</b>
```typescript
fieldAttrs: FieldAttrs;
```

View file

@ -10,6 +10,7 @@ Returns index pattern as saved object body for saving
```typescript
getAsSavedObjectBody(): {
fieldAttrs: string | undefined;
title: string;
timeFieldName: string | undefined;
intervalName: string | undefined;
@ -23,6 +24,7 @@ getAsSavedObjectBody(): {
<b>Returns:</b>
`{
fieldAttrs: string | undefined;
title: string;
timeFieldName: string | undefined;
intervalName: string | undefined;

View file

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) &gt; [getFieldAttrs](./kibana-plugin-plugins-data-server.indexpattern.getfieldattrs.md)
## IndexPattern.getFieldAttrs property
<b>Signature:</b>
```typescript
getFieldAttrs: () => {
[x: string]: {
customName: string;
};
};
```

View file

@ -10,6 +10,7 @@ Get last saved saved object fields
```typescript
getOriginalSavedObjectBody: () => {
fieldAttrs?: string | undefined;
title?: string | undefined;
timeFieldName?: string | undefined;
intervalName?: string | undefined;

View file

@ -21,12 +21,14 @@ export declare class IndexPattern implements IIndexPattern
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [deleteFieldFormat](./kibana-plugin-plugins-data-server.indexpattern.deletefieldformat.md) | | <code>(fieldName: string) =&gt; void</code> | |
| [fieldAttrs](./kibana-plugin-plugins-data-server.indexpattern.fieldattrs.md) | | <code>FieldAttrs</code> | |
| [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md) | | <code>Record&lt;string, any&gt;</code> | |
| [fields](./kibana-plugin-plugins-data-server.indexpattern.fields.md) | | <code>IIndexPatternFieldList &amp; {</code><br/><code> toSpec: () =&gt; IndexPatternFieldMap;</code><br/><code> }</code> | |
| [flattenHit](./kibana-plugin-plugins-data-server.indexpattern.flattenhit.md) | | <code>(hit: Record&lt;string, any&gt;, deep?: boolean) =&gt; Record&lt;string, any&gt;</code> | |
| [formatField](./kibana-plugin-plugins-data-server.indexpattern.formatfield.md) | | <code>FormatFieldFn</code> | |
| [formatHit](./kibana-plugin-plugins-data-server.indexpattern.formathit.md) | | <code>{</code><br/><code> (hit: Record&lt;string, any&gt;, type?: string): any;</code><br/><code> formatField: FormatFieldFn;</code><br/><code> }</code> | |
| [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.getoriginalsavedobjectbody.md) | | <code>() =&gt; {</code><br/><code> title?: string &#124; undefined;</code><br/><code> timeFieldName?: string &#124; undefined;</code><br/><code> intervalName?: string &#124; undefined;</code><br/><code> fields?: string &#124; undefined;</code><br/><code> sourceFilters?: string &#124; undefined;</code><br/><code> fieldFormatMap?: string &#124; undefined;</code><br/><code> typeMeta?: string &#124; undefined;</code><br/><code> type?: string &#124; undefined;</code><br/><code> }</code> | Get last saved saved object fields |
| [getFieldAttrs](./kibana-plugin-plugins-data-server.indexpattern.getfieldattrs.md) | | <code>() =&gt; {</code><br/><code> [x: string]: {</code><br/><code> customName: string;</code><br/><code> };</code><br/><code> }</code> | |
| [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.getoriginalsavedobjectbody.md) | | <code>() =&gt; {</code><br/><code> fieldAttrs?: string &#124; undefined;</code><br/><code> title?: string &#124; undefined;</code><br/><code> timeFieldName?: string &#124; undefined;</code><br/><code> intervalName?: string &#124; undefined;</code><br/><code> fields?: string &#124; undefined;</code><br/><code> sourceFilters?: string &#124; undefined;</code><br/><code> fieldFormatMap?: string &#124; undefined;</code><br/><code> typeMeta?: string &#124; undefined;</code><br/><code> type?: string &#124; undefined;</code><br/><code> }</code> | Get last saved saved object fields |
| [id](./kibana-plugin-plugins-data-server.indexpattern.id.md) | | <code>string</code> | |
| [intervalName](./kibana-plugin-plugins-data-server.indexpattern.intervalname.md) | | <code>string &#124; undefined</code> | |
| [metaFields](./kibana-plugin-plugins-data-server.indexpattern.metafields.md) | | <code>string[]</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) &gt; [fieldAttrs](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldattrs.md)
## IndexPatternAttributes.fieldAttrs property
<b>Signature:</b>
```typescript
fieldAttrs?: string;
```

View file

@ -14,6 +14,7 @@ export interface IndexPatternAttributes
| Property | Type | Description |
| --- | --- | --- |
| [fieldAttrs](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldattrs.md) | <code>string</code> | |
| [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldformatmap.md) | <code>string</code> | |
| [fields](./kibana-plugin-plugins-data-server.indexpatternattributes.fields.md) | <code>string</code> | |
| [intervalName](./kibana-plugin-plugins-data-server.indexpatternattributes.intervalname.md) | <code>string</code> | |

View file

@ -47,6 +47,7 @@ Object {
],
},
"count": 1,
"customName": undefined,
"esTypes": Array [
"text",
],
@ -62,6 +63,7 @@ Object {
"script": "script",
"scripted": true,
"searchable": true,
"shortDotsEnable": undefined,
"subType": Object {
"multi": Object {
"parent": "parent",

View file

@ -22,7 +22,6 @@ import { IFieldType } from './types';
import { IndexPatternField } from './index_pattern_field';
import { FieldSpec, IndexPatternFieldMap } from '../types';
import { IndexPattern } from '../index_patterns';
import { shortenDottedString } from '../../utils';
type FieldMap = Map<IndexPatternField['name'], IndexPatternField>;
@ -58,8 +57,7 @@ export const fieldList = (
this.groups.get(field.type)!.set(field.name, field);
};
private removeByGroup = (field: IFieldType) => this.groups.get(field.type)!.delete(field.name);
private calcDisplayName = (name: string) =>
shortDotsEnable ? shortenDottedString(name) : name;
constructor() {
super();
specs.map((field) => this.add(field));
@ -71,7 +69,7 @@ export const fieldList = (
...(this.groups.get(type) || new Map()).values(),
];
public readonly add = (field: FieldSpec) => {
const newField = new IndexPatternField(field, this.calcDisplayName(field.name));
const newField = new IndexPatternField({ ...field, shortDotsEnable });
this.push(newField);
this.setByName(newField);
this.setByGroup(newField);
@ -86,7 +84,7 @@ export const fieldList = (
};
public readonly update = (field: FieldSpec) => {
const newField = new IndexPatternField(field, this.calcDisplayName(field.name));
const newField = new IndexPatternField(field);
const index = this.findIndex((f) => f.name === newField.name);
this.splice(index, 1, newField);
this.setByName(newField);

View file

@ -28,7 +28,7 @@ describe('Field', function () {
}
function getField(values = {}) {
return new IndexPatternField({ ...fieldValues, ...values }, 'displayName');
return new IndexPatternField({ ...fieldValues, ...values });
}
const fieldValues = {
@ -150,12 +150,12 @@ describe('Field', function () {
});
it('exports the property to JSON', () => {
const field = new IndexPatternField(fieldValues, 'displayName');
const field = new IndexPatternField(fieldValues);
expect(flatten(field)).toMatchSnapshot();
});
it('spec snapshot', () => {
const field = new IndexPatternField(fieldValues, 'displayName');
const field = new IndexPatternField(fieldValues);
const getFormatterForField = () =>
({
toJSON: () => ({

View file

@ -21,16 +21,15 @@ import { KbnFieldType, getKbnFieldType } from '../../kbn_field_types';
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
import { IFieldType } from './types';
import { FieldSpec, IndexPattern } from '../..';
import { shortenDottedString } from '../../utils';
export class IndexPatternField implements IFieldType {
readonly spec: FieldSpec;
// not writable or serialized
readonly displayName: string;
private readonly kbnFieldType: KbnFieldType;
constructor(spec: FieldSpec, displayName: string) {
constructor(spec: FieldSpec) {
this.spec = { ...spec, type: spec.name === '_source' ? '_source' : spec.type };
this.displayName = displayName;
this.kbnFieldType = getKbnFieldType(spec.type);
}
@ -69,6 +68,14 @@ export class IndexPatternField implements IFieldType {
this.spec.lang = lang;
}
public get customName() {
return this.spec.customName;
}
public set customName(label) {
this.spec.customName = label;
}
/**
* Description of field type conflicts across different indices in the same index pattern
*/
@ -85,6 +92,14 @@ export class IndexPatternField implements IFieldType {
return this.spec.name;
}
public get displayName(): string {
return this.spec.customName
? this.spec.customName
: this.spec.shortDotsEnable
? shortenDottedString(this.spec.name)
: this.spec.name;
}
public get type() {
return this.spec.type;
}
@ -140,7 +155,6 @@ export class IndexPatternField implements IFieldType {
script: this.script,
lang: this.lang,
conflictDescriptions: this.conflictDescriptions,
name: this.name,
type: this.type,
esTypes: this.esTypes,
@ -149,6 +163,7 @@ export class IndexPatternField implements IFieldType {
aggregatable: this.aggregatable,
readFromDocValues: this.readFromDocValues,
subType: this.subType,
customName: this.customName,
};
}
@ -171,6 +186,8 @@ export class IndexPatternField implements IFieldType {
readFromDocValues: this.readFromDocValues,
subType: this.subType,
format: getFormatterForField ? getFormatterForField(this).toJSON() : undefined,
customName: this.customName,
shortDotsEnable: this.spec.shortDotsEnable,
};
}
}

View file

@ -37,6 +37,7 @@ export interface IFieldType {
scripted?: boolean;
subType?: IFieldSubType;
displayName?: string;
customName?: string;
format?: any;
toSpec?: (options?: { getFormatterForField?: IndexPattern['getFormatterForField'] }) => FieldSpec;
}

View file

@ -2,12 +2,14 @@
exports[`IndexPattern toSpec should match snapshot 1`] = `
Object {
"fieldAttrs": Object {},
"fieldFormats": Object {},
"fields": Object {
"@tags": Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"keyword",
],
@ -23,6 +25,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -30,6 +33,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 30,
"customName": undefined,
"esTypes": Array [
"date",
],
@ -45,6 +49,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "date",
},
@ -52,6 +57,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"_id",
],
@ -67,6 +73,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -74,6 +81,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"_source",
],
@ -89,6 +97,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "_source",
},
@ -96,6 +105,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"_type",
],
@ -111,6 +121,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -118,6 +129,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"geo_shape",
],
@ -133,6 +145,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "geo_shape",
},
@ -140,6 +153,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 10,
"customName": undefined,
"esTypes": Array [
"long",
],
@ -155,6 +169,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "number",
},
@ -162,6 +177,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"conflict",
],
@ -177,6 +193,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "conflict",
},
@ -184,6 +201,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"text",
],
@ -199,6 +217,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -206,6 +225,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"keyword",
],
@ -221,6 +241,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": Object {
"multi": Object {
"parent": "extension",
@ -232,6 +253,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"geo_point",
],
@ -247,6 +269,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "geo_point",
},
@ -254,6 +277,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"keyword",
],
@ -269,6 +293,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -276,6 +301,7 @@ Object {
"aggregatable": false,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"murmur3",
],
@ -291,6 +317,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "murmur3",
},
@ -298,6 +325,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"ip",
],
@ -313,6 +341,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "ip",
},
@ -320,6 +349,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"text",
],
@ -335,6 +365,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -342,6 +373,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"keyword",
],
@ -357,6 +389,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": Object {
"multi": Object {
"parent": "machine.os",
@ -368,6 +401,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"text",
],
@ -383,6 +417,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": false,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -390,6 +425,7 @@ Object {
"aggregatable": false,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"text",
],
@ -405,6 +441,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": false,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -412,6 +449,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"integer",
],
@ -427,6 +465,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "number",
},
@ -434,6 +473,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"geo_point",
],
@ -449,6 +489,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "geo_point",
},
@ -456,6 +497,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"attachment",
],
@ -471,6 +513,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "attachment",
},
@ -478,6 +521,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"date",
],
@ -493,6 +537,7 @@ Object {
"script": "1234",
"scripted": true,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "date",
},
@ -500,6 +545,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"murmur3",
],
@ -515,6 +561,7 @@ Object {
"script": "1234",
"scripted": true,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "murmur3",
},
@ -522,6 +569,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"long",
],
@ -537,6 +585,7 @@ Object {
"script": "1234",
"scripted": true,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "number",
},
@ -544,6 +593,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"text",
],
@ -559,6 +609,7 @@ Object {
"script": "'i am a string'",
"scripted": true,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "string",
},
@ -566,6 +617,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 20,
"customName": undefined,
"esTypes": Array [
"boolean",
],
@ -581,6 +633,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "boolean",
},
@ -588,6 +641,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 30,
"customName": undefined,
"esTypes": Array [
"date",
],
@ -603,6 +657,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "date",
},
@ -610,6 +665,7 @@ Object {
"aggregatable": true,
"conflictDescriptions": undefined,
"count": 0,
"customName": undefined,
"esTypes": Array [
"date",
],
@ -625,6 +681,7 @@ Object {
"script": undefined,
"scripted": false,
"searchable": true,
"shortDotsEnable": false,
"subType": undefined,
"type": "date",
},

View file

@ -2,6 +2,7 @@
exports[`IndexPatterns savedObjectToSpec 1`] = `
Object {
"fieldAttrs": Object {},
"fieldFormats": Object {
"field": Object {},
},

View file

@ -18,6 +18,7 @@
*/
import _, { each, reject } from 'lodash';
import { FieldAttrs } from '../..';
import { DuplicateField } from '../../../../kibana_utils/common';
import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common';
@ -36,6 +37,7 @@ interface IndexPatternDeps {
}
interface SavedObjectBody {
fieldAttrs?: string;
title?: string;
timeFieldName?: string;
intervalName?: string;
@ -70,6 +72,8 @@ export class IndexPattern implements IIndexPattern {
private originalSavedObjectBody: SavedObjectBody = {};
private shortDotsEnable: boolean = false;
private fieldFormats: FieldFormatsStartCommon;
// make private once manual field refresh is removed
public fieldAttrs: FieldAttrs;
constructor({
spec = {},
@ -101,10 +105,10 @@ export class IndexPattern implements IIndexPattern {
this.title = spec.title || '';
this.timeFieldName = spec.timeFieldName;
this.sourceFilters = spec.sourceFilters;
this.fields.replaceAll(Object.values(spec.fields || {}));
this.type = spec.type;
this.typeMeta = spec.typeMeta;
this.fieldAttrs = spec.fieldAttrs || {};
}
setFieldFormat = (fieldName: string, format: SerializedFieldFormat) => {
@ -127,6 +131,20 @@ export class IndexPattern implements IIndexPattern {
this.originalSavedObjectBody = this.getAsSavedObjectBody();
};
getFieldAttrs = () => {
const newFieldAttrs = { ...this.fieldAttrs };
this.fields.forEach((field) => {
if (field.customName) {
newFieldAttrs[field.name] = { customName: field.customName };
} else {
delete newFieldAttrs[field.name];
}
});
return newFieldAttrs;
};
getComputedFields() {
const scriptFields: any = {};
if (!this.fields) {
@ -180,6 +198,7 @@ export class IndexPattern implements IIndexPattern {
typeMeta: this.typeMeta,
type: this.type,
fieldFormats: this.fieldFormatMap,
fieldAttrs: this.fieldAttrs,
};
}
@ -271,8 +290,10 @@ export class IndexPattern implements IIndexPattern {
const fieldFormatMap = _.isEmpty(this.fieldFormatMap)
? undefined
: JSON.stringify(this.fieldFormatMap);
const fieldAttrs = this.getFieldAttrs();
return {
fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined,
title: this.title,
timeFieldName: this.timeFieldName,
intervalName: this.intervalName,

View file

@ -35,6 +35,7 @@ import {
GetFieldsOptions,
IndexPatternSpec,
IndexPatternAttributes,
FieldAttrs,
FieldSpec,
IndexPatternFieldMap,
} from '../types';
@ -249,7 +250,11 @@ export class IndexPatternsService {
try {
const fields = await this.getFieldsForIndexPattern(indexPattern);
const scripted = indexPattern.getScriptedFields().map((field) => field.spec);
indexPattern.fields.replaceAll([...fields, ...scripted]);
const fieldAttrs = indexPattern.getFieldAttrs();
const fieldsWithSavedAttrs = Object.values(
this.fieldArrayToMap([...fields, ...scripted], fieldAttrs)
);
indexPattern.fields.replaceAll(fieldsWithSavedAttrs);
} catch (err) {
if (err instanceof IndexPatternMissingIndices) {
this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' });
@ -275,12 +280,13 @@ export class IndexPatternsService {
fields: IndexPatternFieldMap,
id: string,
title: string,
options: GetFieldsOptions
options: GetFieldsOptions,
fieldAttrs: FieldAttrs = {}
) => {
const scriptdFields = Object.values(fields).filter((field) => field.scripted);
try {
const newFields = await this.getFieldsForWildcard(options);
return this.fieldArrayToMap([...newFields, ...scriptdFields]);
const newFields = (await this.getFieldsForWildcard(options)) as FieldSpec[];
return this.fieldArrayToMap([...newFields, ...scriptdFields], fieldAttrs);
} catch (err) {
if (err instanceof IndexPatternMissingIndices) {
this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' });
@ -301,9 +307,9 @@ export class IndexPatternsService {
* Converts field array to map
* @param fields
*/
fieldArrayToMap = (fields: FieldSpec[]) =>
fieldArrayToMap = (fields: FieldSpec[], fieldAttrs?: FieldAttrs) =>
fields.reduce<IndexPatternFieldMap>((collector, field) => {
collector[field.name] = field;
collector[field.name] = { ...field, customName: fieldAttrs?.[field.name]?.customName };
return collector;
}, {});
@ -325,6 +331,7 @@ export class IndexPatternsService {
fieldFormatMap,
typeMeta,
type,
fieldAttrs,
},
} = savedObject;
@ -332,6 +339,7 @@ export class IndexPatternsService {
const parsedTypeMeta = typeMeta ? JSON.parse(typeMeta) : undefined;
const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {};
const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : [];
const parsedFieldAttrs: FieldAttrs = fieldAttrs ? JSON.parse(fieldAttrs) : {};
return {
id,
@ -340,10 +348,11 @@ export class IndexPatternsService {
intervalName,
timeFieldName,
sourceFilters: parsedSourceFilters,
fields: this.fieldArrayToMap(parsedFields),
fields: this.fieldArrayToMap(parsedFields, parsedFieldAttrs),
typeMeta: parsedTypeMeta,
type,
fieldFormats: parsedFieldFormatMap,
fieldAttrs: parsedFieldAttrs,
};
};
@ -369,17 +378,26 @@ export class IndexPatternsService {
const spec = this.savedObjectToSpec(savedObject);
const { title, type, typeMeta } = spec;
spec.fieldAttrs = savedObject.attributes.fieldAttrs
? JSON.parse(savedObject.attributes.fieldAttrs)
: {};
const isFieldRefreshRequired = this.isFieldRefreshRequired(spec.fields);
let isSaveRequired = isFieldRefreshRequired;
try {
spec.fields = isFieldRefreshRequired
? await this.refreshFieldSpecMap(spec.fields || {}, id, spec.title as string, {
pattern: title as string,
metaFields: await this.config.get(UI_SETTINGS.META_FIELDS),
type,
rollupIndex: typeMeta?.params?.rollupIndex,
})
? await this.refreshFieldSpecMap(
spec.fields || {},
id,
spec.title as string,
{
pattern: title as string,
metaFields: await this.config.get(UI_SETTINGS.META_FIELDS),
type,
rollupIndex: typeMeta?.params?.rollupIndex,
},
spec.fieldAttrs
)
: spec.fields;
} catch (err) {
isSaveRequired = false;

View file

@ -48,6 +48,11 @@ export interface IndexPatternAttributes {
intervalName?: string;
sourceFilters?: string;
fieldFormatMap?: string;
fieldAttrs?: string;
}
export interface FieldAttrs {
[key: string]: { customName: string };
}
export type OnNotification = (toastInputFields: ToastInputFields) => void;
@ -155,7 +160,6 @@ export interface FieldSpec {
lang?: string;
conflictDescriptions?: Record<string, string[]>;
format?: SerializedFieldFormat;
name: string;
type: string;
esTypes?: string[];
@ -165,6 +169,9 @@ export interface FieldSpec {
readFromDocValues?: boolean;
subType?: IFieldSubType;
indexed?: boolean;
customName?: string;
// not persisted
shortDotsEnable?: boolean;
}
export type IndexPatternFieldMap = Record<string, FieldSpec>;
@ -180,6 +187,7 @@ export interface IndexPatternSpec {
typeMeta?: TypeMeta;
type?: string;
fieldFormats?: Record<string, SerializedFieldFormat>;
fieldAttrs?: FieldAttrs;
}
export interface SourceFilter {

View file

@ -975,6 +975,8 @@ export interface IFieldType {
// (undocumented)
count?: number;
// (undocumented)
customName?: string;
// (undocumented)
displayName?: string;
// (undocumented)
esTypes?: string[];
@ -1096,6 +1098,10 @@ export class IndexPattern implements IIndexPattern {
addScriptedField(name: string, script: string, fieldType?: string): Promise<void>;
// (undocumented)
deleteFieldFormat: (fieldName: string) => void;
// Warning: (ae-forgotten-export) The symbol "FieldAttrs" needs to be exported by the entry point index.d.ts
//
// (undocumented)
fieldAttrs: FieldAttrs;
// (undocumented)
fieldFormatMap: Record<string, any>;
// (undocumented)
@ -1121,6 +1127,7 @@ export class IndexPattern implements IIndexPattern {
time_zone?: string | undefined;
}>> | undefined;
getAsSavedObjectBody(): {
fieldAttrs: string | undefined;
title: string;
timeFieldName: string | undefined;
intervalName: string | undefined;
@ -1140,12 +1147,19 @@ export class IndexPattern implements IIndexPattern {
}[];
};
// (undocumented)
getFieldAttrs: () => {
[x: string]: {
customName: string;
};
};
// (undocumented)
getFieldByName(name: string): IndexPatternField | undefined;
getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat;
getFormatterForFieldNoDefault(fieldname: string): FieldFormat | undefined;
// (undocumented)
getNonScriptedFields(): IndexPatternField[];
getOriginalSavedObjectBody: () => {
fieldAttrs?: string | undefined;
title?: string | undefined;
timeFieldName?: string | undefined;
intervalName?: string | undefined;
@ -1210,6 +1224,8 @@ export type IndexPatternAggRestrictions = Record<string, {
//
// @public (undocumented)
export interface IndexPatternAttributes {
// (undocumented)
fieldAttrs?: string;
// (undocumented)
fieldFormatMap?: string;
// (undocumented)
@ -1232,7 +1248,7 @@ export interface IndexPatternAttributes {
//
// @public (undocumented)
export class IndexPatternField implements IFieldType {
constructor(spec: FieldSpec, displayName: string);
constructor(spec: FieldSpec);
// (undocumented)
get aggregatable(): boolean;
get conflictDescriptions(): Record<string, string[]> | undefined;
@ -1240,7 +1256,10 @@ export class IndexPatternField implements IFieldType {
get count(): number;
set count(count: number);
// (undocumented)
readonly displayName: string;
get customName(): string | undefined;
set customName(label: string | undefined);
// (undocumented)
get displayName(): string;
// (undocumented)
get esTypes(): string[] | undefined;
// (undocumented)
@ -1277,6 +1296,7 @@ export class IndexPatternField implements IFieldType {
aggregatable: boolean;
readFromDocValues: boolean;
subType: import("../types").IFieldSubType | undefined;
customName: string | undefined;
};
// (undocumented)
toSpec({ getFormatterForField, }?: {
@ -1324,6 +1344,8 @@ export type IndexPatternSelectProps = Required<Omit<EuiComboBoxProps<any>, 'isLo
//
// @public (undocumented)
export interface IndexPatternSpec {
// (undocumented)
fieldAttrs?: FieldAttrs;
// (undocumented)
fieldFormats?: Record<string, SerializedFieldFormat>;
// (undocumented)
@ -1361,7 +1383,7 @@ export class IndexPatternsService {
//
// (undocumented)
ensureDefaultIndexPattern: EnsureDefaultIndexPattern;
fieldArrayToMap: (fields: FieldSpec[]) => Record<string, FieldSpec>;
fieldArrayToMap: (fields: FieldSpec[], fieldAttrs?: FieldAttrs | undefined) => Record<string, FieldSpec>;
get: (id: string) => Promise<IndexPattern>;
// Warning: (ae-forgotten-export) The symbol "IndexPatternSavedObjectAttrs" needs to be exported by the entry point index.d.ts
//
@ -2325,7 +2347,7 @@ export const UI_SETTINGS: {
// src/plugins/data/common/es_query/filters/meta_filter.ts:54:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:62: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:64:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/search/aggs/types.ts:113:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/field_formats/field_formats_service.ts:67:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts

View file

@ -507,6 +507,8 @@ export interface IFieldType {
// (undocumented)
count?: number;
// (undocumented)
customName?: string;
// (undocumented)
displayName?: string;
// (undocumented)
esTypes?: string[];
@ -557,6 +559,10 @@ export class IndexPattern implements IIndexPattern {
addScriptedField(name: string, script: string, fieldType?: string): Promise<void>;
// (undocumented)
deleteFieldFormat: (fieldName: string) => void;
// Warning: (ae-forgotten-export) The symbol "FieldAttrs" needs to be exported by the entry point index.d.ts
//
// (undocumented)
fieldAttrs: FieldAttrs;
// (undocumented)
fieldFormatMap: Record<string, any>;
// Warning: (ae-forgotten-export) The symbol "IIndexPatternFieldList" needs to be exported by the entry point index.d.ts
@ -584,6 +590,7 @@ export class IndexPattern implements IIndexPattern {
time_zone?: string | undefined;
}>> | undefined;
getAsSavedObjectBody(): {
fieldAttrs: string | undefined;
title: string;
timeFieldName: string | undefined;
intervalName: string | undefined;
@ -603,6 +610,12 @@ export class IndexPattern implements IIndexPattern {
}[];
};
// (undocumented)
getFieldAttrs: () => {
[x: string]: {
customName: string;
};
};
// (undocumented)
getFieldByName(name: string): IndexPatternField | undefined;
getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat;
getFormatterForFieldNoDefault(fieldname: string): FieldFormat | undefined;
@ -611,6 +624,7 @@ export class IndexPattern implements IIndexPattern {
// (undocumented)
getNonScriptedFields(): IndexPatternField[];
getOriginalSavedObjectBody: () => {
fieldAttrs?: string | undefined;
title?: string | undefined;
timeFieldName?: string | undefined;
intervalName?: string | undefined;
@ -669,6 +683,8 @@ export class IndexPattern implements IIndexPattern {
//
// @public (undocumented)
export interface IndexPatternAttributes {
// (undocumented)
fieldAttrs?: string;
// (undocumented)
fieldFormatMap?: string;
// (undocumented)
@ -1195,8 +1211,8 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
//
// src/plugins/data/common/es_query/filters/meta_filter.ts:53:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/es_query/filters/meta_filter.ts:54:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts
// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:56: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:62: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:58: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:64:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:40:23 - (ae-forgotten-export) The symbol "buildCustomFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:40:23 - (ae-forgotten-export) The symbol "buildFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:71:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts

View file

@ -17,7 +17,6 @@
* under the License.
*/
import { IndexPattern } from '../../../../../kibana_services';
import { shortenDottedString } from '../../../../helpers';
export type SortOrder = [string, string];
export interface ColumnProps {
@ -67,7 +66,7 @@ export function getDisplayedColumns(
const field = indexPattern.getFieldByName(column);
return {
name: column,
displayName: isShortDots ? shortenDottedString(column) : column,
displayName: field ? field.displayName : column,
isSortable: field && field.sortable ? true : false,
isRemoveable: column !== '_source' || columns.length > 1,
colLeftIdx: idx - 1 < 0 ? -1 : idx - 1,

View file

@ -35,6 +35,7 @@ function getMockIndexPattern() {
if (name === 'test1') {
return {
name,
displayName: name,
type: 'string',
aggregatable: false,
searchable: true,
@ -43,6 +44,7 @@ function getMockIndexPattern() {
} else {
return {
name,
displayName: name,
type: 'string',
aggregatable: false,
searchable: true,

View file

@ -24,7 +24,7 @@ import { SortOrder } from './helpers';
interface Props {
colLeftIdx: number; // idx of the column to the left, -1 if moving is not possible
colRightIdx: number; // idx of the column to the right, -1 if moving is not possible
displayName: string;
displayName?: string;
isRemoveable: boolean;
isSortable: boolean;
name: string;

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FieldName renders a geo field, useShortDots is set to true 1`] = `
exports[`FieldName renders a geo field 1`] = `
<div
class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
>
@ -24,7 +24,7 @@ exports[`FieldName renders a geo field, useShortDots is set to true 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
<span>
t.t.test
test.test.test
</span>
</span>
</div>

View file

@ -32,9 +32,7 @@ test('FieldName renders a number field by providing a field record, useShortDots
expect(component).toMatchSnapshot();
});
test('FieldName renders a geo field, useShortDots is set to true', () => {
const component = render(
<FieldName fieldName={'test.test.test'} fieldType={'geo_point'} useShortDots={true} />
);
test('FieldName renders a geo field', () => {
const component = render(<FieldName fieldName={'test.test.test'} fieldType={'geo_point'} />);
expect(component).toMatchSnapshot();
});

View file

@ -18,30 +18,31 @@
*/
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { FieldIcon, FieldIconProps } from '../../../../../kibana_react/public';
import { shortenDottedString } from '../../helpers';
import { getFieldTypeName } from './field_type_name';
import { FieldMapping } from '../../doc_views/doc_views_types';
// properties fieldType and fieldName are provided in kbn_doc_view
// this should be changed when both components are deangularized
interface Props {
fieldName: string;
fieldType: string;
useShortDots?: boolean;
fieldMapping?: FieldMapping;
fieldIconProps?: Omit<FieldIconProps, 'type'>;
scripted?: boolean;
}
export function FieldName({
fieldName,
fieldMapping,
fieldType,
useShortDots,
fieldIconProps,
scripted = false,
}: Props) {
const typeName = getFieldTypeName(fieldType);
const displayName = useShortDots ? shortenDottedString(fieldName) : fieldName;
const displayName =
fieldMapping && fieldMapping.displayName ? fieldMapping.displayName : fieldName;
const tooltip = displayName !== fieldName ? `${fieldName} (${displayName})` : fieldName;
return (
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
@ -51,7 +52,7 @@ export function FieldName({
<EuiFlexItem className="eui-textTruncate">
<EuiToolTip
position="top"
content={displayName}
content={tooltip}
delay="long"
anchorClassName="eui-textTruncate"
>

View file

@ -43,8 +43,6 @@ jest.mock('../../../kibana_services', () => ({
get: (key: string) => {
if (key === 'fields:popularLimit') {
return 5;
} else if (key === 'shortDots:enable') {
return false;
}
},
},
@ -54,7 +52,6 @@ jest.mock('../../../kibana_services', () => ({
function getComponent({
selected = false,
showDetails = false,
useShortDots = false,
field,
}: {
selected?: boolean;
@ -72,19 +69,16 @@ function getComponent({
const finalField =
field ??
new IndexPatternField(
{
name: 'bytes',
type: 'number',
esTypes: ['long'],
count: 10,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
},
'bytes'
);
new IndexPatternField({
name: 'bytes',
type: 'number',
esTypes: ['long'],
count: 10,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
});
const props = {
indexPattern,
@ -95,7 +89,6 @@ function getComponent({
onRemoveField: jest.fn(),
showDetails,
selected,
useShortDots,
};
const comp = mountWithIntl(<DiscoverField {...props} />);
return { comp, props };
@ -118,17 +111,14 @@ describe('discover sidebar field', function () {
expect(props.getDetails).toHaveBeenCalledWith(props.field);
});
it('should not allow clicking on _source', function () {
const field = new IndexPatternField(
{
name: '_source',
type: '_source',
esTypes: ['_source'],
searchable: true,
aggregatable: true,
readFromDocValues: true,
},
'_source'
);
const field = new IndexPatternField({
name: '_source',
type: '_source',
esTypes: ['_source'],
searchable: true,
aggregatable: true,
readFromDocValues: true,
});
const { comp, props } = getComponent({
selected: true,
field,

View file

@ -24,7 +24,6 @@ import { DiscoverFieldDetails } from './discover_field_details';
import { FieldIcon, FieldButton } from '../../../../../kibana_react/public';
import { FieldDetails } from './types';
import { IndexPatternField, IndexPattern } from '../../../../../data/public';
import { shortenDottedString } from '../../helpers';
import { getFieldTypeName } from './lib/get_field_type_name';
import './discover_field.scss';
@ -58,10 +57,6 @@ export interface DiscoverFieldProps {
* Determines whether the field is selected
*/
selected?: boolean;
/**
* Determines whether the field name is shortened test.sub1.sub2 = t.s.sub2
*/
useShortDots?: boolean;
/**
* Metric tracking function
* @param metricType
@ -78,7 +73,6 @@ export function DiscoverField({
onAddFilter,
getDetails,
selected,
useShortDots,
trackUiMetric,
}: DiscoverFieldProps) {
const addLabelAria = i18n.translate('discover.fieldChooser.discoverField.addButtonAriaLabel', {
@ -118,13 +112,12 @@ export function DiscoverField({
<FieldIcon type={field.type} label={getFieldTypeName(field.type)} scripted={field.scripted} />
);
const title =
field.displayName !== field.name ? `${field.name} (${field.displayName} )` : field.displayName;
const fieldName = (
<span
data-test-subj={`field-${field.name}`}
title={field.name}
className="dscSidebarField__name"
>
{useShortDots ? wrapOnDot(shortenDottedString(field.name)) : wrapOnDot(field.displayName)}
<span data-test-subj={`field-${field.name}`} title={title} className="dscSidebarField__name">
{wrapOnDot(field.displayName)}
</span>
);

View file

@ -48,55 +48,46 @@ describe('discover sidebar field details', function () {
}
it('should enable the visualize link for a number field', function () {
const visualizableField = new IndexPatternField(
{
name: 'bytes',
type: 'number',
esTypes: ['long'],
count: 10,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
},
'bytes'
);
const visualizableField = new IndexPatternField({
name: 'bytes',
type: 'number',
esTypes: ['long'],
count: 10,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
});
const comp = mountComponent(visualizableField);
expect(findTestSubject(comp, 'fieldVisualize-bytes')).toBeTruthy();
});
it('should disable the visualize link for an _id field', function () {
const conflictField = new IndexPatternField(
{
name: '_id',
type: 'string',
esTypes: ['_id'],
count: 0,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
},
'test'
);
const conflictField = new IndexPatternField({
name: '_id',
type: 'string',
esTypes: ['_id'],
count: 0,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
});
const comp = mountComponent(conflictField);
expect(findTestSubject(comp, 'fieldVisualize-_id')).toEqual({});
});
it('should disable the visualize link for an unknown field', function () {
const unknownField = new IndexPatternField(
{
name: 'test',
type: 'unknown',
esTypes: ['double'],
count: 0,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
},
'test'
);
const unknownField = new IndexPatternField({
name: 'test',
type: 'unknown',
esTypes: ['double'],
count: 0,
scripted: false,
searchable: true,
aggregatable: true,
readFromDocValues: true,
});
const comp = mountComponent(unknownField);
expect(findTestSubject(comp, 'fieldVisualize-test')).toEqual({});
});

View file

@ -51,8 +51,6 @@ jest.mock('../../../kibana_services', () => ({
get: (key: string) => {
if (key === 'fields:popularLimit') {
return 5;
} else if (key === 'shortDots:enable') {
return false;
}
},
},

View file

@ -30,7 +30,7 @@ import { IndexPatternAttributes } from '../../../../../data/common';
import { SavedObject } from '../../../../../../core/types';
import { FIELDS_LIMIT_SETTING } from '../../../../common';
import { groupFields } from './lib/group_fields';
import { IndexPatternField, IndexPattern, UI_SETTINGS } from '../../../../../data/public';
import { IndexPatternField, IndexPattern } from '../../../../../data/public';
import { getDetails } from './lib/get_details';
import { getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter';
import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list';
@ -117,7 +117,6 @@ export function DiscoverSidebar({
);
const popularLimit = services.uiSettings.get(FIELDS_LIMIT_SETTING);
const useShortDots = services.uiSettings.get(UI_SETTINGS.SHORT_DOTS_ENABLE);
const {
selected: selectedFields,
@ -201,7 +200,6 @@ export function DiscoverSidebar({
onAddFilter={onAddFilter}
getDetails={getDetailsByField}
selected={true}
useShortDots={useShortDots}
trackUiMetric={trackUiMetric}
/>
</li>
@ -276,7 +274,6 @@ export function DiscoverSidebar({
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
getDetails={getDetailsByField}
useShortDots={useShortDots}
trackUiMetric={trackUiMetric}
/>
</li>
@ -307,7 +304,6 @@ export function DiscoverSidebar({
onRemoveField={onRemoveField}
onAddFilter={onAddFilter}
getDetails={getDetailsByField}
useShortDots={useShortDots}
trackUiMetric={trackUiMetric}
/>
</li>

View file

@ -59,6 +59,7 @@ describe('field_filter', function () {
const fieldList = [
{
name: 'bytes',
displayName: 'Bye,bye,Bytes',
type: 'number',
esTypes: ['long'],
count: 10,
@ -68,6 +69,7 @@ describe('field_filter', function () {
},
{
name: 'extension',
displayName: 'Extension',
type: 'string',
esTypes: ['text'],
count: 10,
@ -80,6 +82,8 @@ describe('field_filter', function () {
[
{ filter: {}, result: ['bytes', 'extension'] },
{ filter: { name: 'by' }, result: ['bytes'] },
{ filter: { name: 'Ext' }, result: ['extension'] },
{ filter: { name: 'Bytes' }, result: ['bytes'] },
{ filter: { aggregatable: true }, result: ['extension'] },
{ filter: { aggregatable: true, searchable: false }, result: [] },
{ filter: { type: 'string' }, result: ['extension'] },

View file

@ -72,7 +72,9 @@ export function isFieldFiltered(
field.type === '_source' ||
field.scripted ||
fieldCounts[field.name] > 0;
const matchName = !filterState.name || field.name.indexOf(filterState.name) !== -1;
const needle = filterState.name ? filterState.name.toLowerCase() : '';
const haystack = `${field.name}${field.displayName || ''}`.toLowerCase();
const matchName = !filterState.name || haystack.indexOf(needle) !== -1;
return matchFilter && isAggregatable && isSearchable && scriptedOrMissing && matchName;
}

View file

@ -46,7 +46,13 @@ export function DocViewTable({
<table className="table table-condensed kbnDocViewerTable">
<tbody>
{Object.keys(flattened)
.sort()
.sort((fieldA, fieldB) => {
const mappingA = mapping(fieldA);
const mappingB = mapping(fieldB);
const nameA = !mappingA || !mappingA.displayName ? fieldA : mappingA.displayName;
const nameB = !mappingB || !mappingB.displayName ? fieldB : mappingB.displayName;
return nameA.localeCompare(nameB);
})
.map((field) => {
const valueRaw = flattened[field];
const value = trimAngularSpan(String(formatted[field]));

View file

@ -91,6 +91,7 @@ export function DocViewTableRow({
<FieldName
fieldName={field}
fieldType={fieldType}
fieldMapping={fieldMapping}
fieldIconProps={{ fill: 'none', color: 'gray' }}
scripted={Boolean(fieldMapping?.scripted)}
/>

View file

@ -36,6 +36,7 @@ export interface FieldMapping {
rowCount?: number;
type: string;
name: string;
displayName?: string;
}
export type DocViewFilterFn = (

View file

@ -17,5 +17,4 @@
* under the License.
*/
export { shortenDottedString } from './shorten_dotted_string';
export { formatNumWithCommas } from './format_number_with_commas';

View file

@ -1,26 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const DOT_PREFIX_RE = /(.).+?\./g;
/**
* Convert a dot.notated.string into a short
* version (d.n.string)
*/
export const shortenDottedString = (input: string) => input.replace(DOT_PREFIX_RE, '$1.');

View file

@ -19,7 +19,14 @@
import React, { PureComponent } from 'react';
import { EuiIcon, EuiInMemoryTable, EuiIconTip, EuiBasicTableColumn } from '@elastic/eui';
import {
EuiIcon,
EuiInMemoryTable,
EuiIconTip,
EuiBasicTableColumn,
EuiBadge,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@ -144,6 +151,11 @@ const editDescription = i18n.translate(
{ defaultMessage: 'Edit' }
);
const customNameDescription = i18n.translate(
'indexPatternManagement.editIndexPattern.fields.table.customNameTooltip',
{ defaultMessage: 'A custom name for the field.' }
);
interface IndexedFieldProps {
indexPattern: IIndexPattern;
items: IndexedFieldItem[];
@ -160,7 +172,7 @@ export class Table extends PureComponent<IndexedFieldProps> {
return (
<span>
{name}
{field.name}
{field.info && field.info.length ? (
<span>
&nbsp;
@ -185,6 +197,15 @@ export class Table extends PureComponent<IndexedFieldProps> {
/>
</span>
) : null}
{field.customName && field.customName !== field.name ? (
<div>
<EuiToolTip content={customNameDescription}>
<EuiBadge iconType="flag" iconSide="left">
{field.customName}
</EuiBadge>
</EuiToolTip>
</div>
) : null}
</span>
);
}

View file

@ -47,10 +47,7 @@ const indexPattern = ({
} as unknown) as IndexPattern;
const mockFieldToIndexPatternField = (spec: Record<string, string | boolean | undefined>) => {
return new IndexPatternField(
(spec as unknown) as IndexPatternField['spec'],
spec.displayName as string
);
return new IndexPatternField((spec as unknown) as IndexPatternField['spec']);
};
const fields = [

View file

@ -89,7 +89,11 @@ export class IndexedFieldsTable extends Component<
(fields, fieldFilter, indexedFieldTypeFilter) => {
if (fieldFilter) {
const normalizedFieldFilter = fieldFilter.toLowerCase();
fields = fields.filter((field) => field.name.toLowerCase().includes(normalizedFieldFilter));
fields = fields.filter(
(field) =>
field.name.toLowerCase().includes(normalizedFieldFilter) ||
(field.displayName && field.displayName.toLowerCase().includes(normalizedFieldFilter))
);
}
if (indexedFieldTypeFilter) {

View file

@ -51,6 +51,22 @@ exports[`FieldEditor should render create new scripted field correctly 1`] = `
value=""
/>
</eui-form-row>
<eui-form-row
helpText={
<FormattedMessage
defaultMessage="Set a custom name to use when this field is displayed in Discover and Visualize. Queries and filters don't currently support a custom name and will use the original field name."
id="indexPatternManagement.customNameHelpText"
values={Object {}}
/>
}
label="Custom name"
>
<eui-field-text
data-test-subj="editorFieldCustomName"
onChange={[Function]}
value=""
/>
</eui-form-row>
<eui-form-row
helpText={null}
label="Language"
@ -156,7 +172,7 @@ exports[`FieldEditor should render create new scripted field correctly 1`] = `
isInvalid={true}
label="Script"
>
<Component
<eui-code-editor
data-test-subj="editorFieldScript"
height="300px"
mode="groovy"
@ -275,6 +291,23 @@ exports[`FieldEditor should render edit scripted field correctly 1`] = `
onClose={[Function]}
script="doc.test.value"
/>
<eui-form-row
helpText={
<FormattedMessage
defaultMessage="Set a custom name to use when this field is displayed in Discover and Visualize. Queries and filters don't currently support a custom name and will use the original field name."
id="indexPatternManagement.customNameHelpText"
values={Object {}}
/>
}
label="Custom name"
>
<eui-field-text
data-test-subj="editorFieldCustomName"
onChange={[Function]}
placeholder="test"
value=""
/>
</eui-form-row>
<eui-form-row
helpText={null}
label="Language"
@ -374,7 +407,7 @@ exports[`FieldEditor should render edit scripted field correctly 1`] = `
isInvalid={false}
label="Script"
>
<Component
<eui-code-editor
data-test-subj="editorFieldScript"
height="300px"
mode="groovy"
@ -550,6 +583,23 @@ exports[`FieldEditor should show conflict field warning 1`] = `
value="foobar"
/>
</eui-form-row>
<eui-form-row
helpText={
<FormattedMessage
defaultMessage="Set a custom name to use when this field is displayed in Discover and Visualize. Queries and filters don't currently support a custom name and will use the original field name."
id="indexPatternManagement.customNameHelpText"
values={Object {}}
/>
}
label="Custom name"
>
<eui-field-text
data-test-subj="editorFieldCustomName"
onChange={[Function]}
placeholder="foobar"
value=""
/>
</eui-form-row>
<eui-form-row
helpText={null}
label="Language"
@ -655,7 +705,7 @@ exports[`FieldEditor should show conflict field warning 1`] = `
isInvalid={true}
label="Script"
>
<Component
<eui-code-editor
data-test-subj="editorFieldScript"
height="300px"
mode="groovy"
@ -774,6 +824,23 @@ exports[`FieldEditor should show deprecated lang warning 1`] = `
onClose={[Function]}
script="doc.test.value"
/>
<eui-form-row
helpText={
<FormattedMessage
defaultMessage="Set a custom name to use when this field is displayed in Discover and Visualize. Queries and filters don't currently support a custom name and will use the original field name."
id="indexPatternManagement.customNameHelpText"
values={Object {}}
/>
}
label="Custom name"
>
<eui-field-text
data-test-subj="editorFieldCustomName"
onChange={[Function]}
placeholder="test"
value=""
/>
</eui-form-row>
<eui-form-row
helpText={
<span>
@ -954,7 +1021,7 @@ exports[`FieldEditor should show deprecated lang warning 1`] = `
isInvalid={false}
label="Script"
>
<Component
<eui-code-editor
data-test-subj="editorFieldScript"
height="300px"
mode="groovy"
@ -1130,6 +1197,23 @@ exports[`FieldEditor should show multiple type field warning with a table contai
value="foobar"
/>
</eui-form-row>
<eui-form-row
helpText={
<FormattedMessage
defaultMessage="Set a custom name to use when this field is displayed in Discover and Visualize. Queries and filters don't currently support a custom name and will use the original field name."
id="indexPatternManagement.customNameHelpText"
values={Object {}}
/>
}
label="Custom name"
>
<eui-field-text
data-test-subj="editorFieldCustomName"
onChange={[Function]}
placeholder="foobar"
value=""
/>
</eui-form-row>
<eui-form-row
helpText={null}
label="Language"
@ -1291,7 +1375,7 @@ exports[`FieldEditor should show multiple type field warning with a table contai
isInvalid={true}
label="Script"
>
<Component
<eui-code-editor
data-test-subj="editorFieldScript"
height="300px"
mode="groovy"

View file

@ -23,6 +23,7 @@ import {
FieldFormatInstanceType,
IndexPatternsService,
} from 'src/plugins/data/public';
import { findTestSubject } from '@elastic/eui/lib/test';
jest.mock('brace/mode/groovy', () => ({}));
@ -37,6 +38,7 @@ jest.mock('@elastic/eui', () => ({
EuiButtonEmpty: 'eui-button-empty',
EuiCallOut: 'eui-call-out',
EuiCode: 'eui-code',
EuiCodeEditor: 'eui-code-editor',
EuiConfirmModal: 'eui-confirm-modal',
EuiFieldNumber: 'eui-field-number',
EuiFieldText: 'eui-field-text',
@ -173,6 +175,60 @@ describe('FieldEditor', () => {
expect(component).toMatchSnapshot();
});
it('should display and update a customName correctly', async () => {
let testField = ({
name: 'test',
format: new Format(),
lang: undefined,
type: 'string',
customName: 'Test',
} as unknown) as IndexPatternField;
fieldList.push(testField);
indexPattern.fields.getByName = (name) => {
const flds = {
[testField.name]: testField,
};
return flds[name];
};
indexPattern.fields = {
...indexPattern.fields,
...{
update: (fld) => {
testField = (fld as unknown) as IndexPatternField;
},
add: jest.fn(),
},
};
indexPattern.fieldFormatMap = { test: field };
indexPattern.deleteFieldFormat = jest.fn();
const component = createComponentWithContext<FieldEdiorProps>(
FieldEditor,
{
indexPattern,
spec: (testField as unknown) as IndexPatternField,
services: {
redirectAway: () => {},
indexPatternService: ({
updateSavedObject: jest.fn(() => Promise.resolve()),
} as unknown) as IndexPatternsService,
},
},
mockContext
);
await new Promise((resolve) => process.nextTick(resolve));
component.update();
const input = findTestSubject(component, 'editorFieldCustomName');
expect(input.props().value).toBe('Test');
input.simulate('change', { target: { value: 'new Test' } });
const saveBtn = findTestSubject(component, 'fieldSaveButton');
await saveBtn.simulate('click');
await new Promise((resolve) => process.nextTick(resolve));
expect(testField.customName).toEqual('new Test');
});
it('should show deprecated lang warning', async () => {
const testField = {
...field,

View file

@ -126,6 +126,7 @@ export interface FieldEditorState {
errors?: string[];
format: any;
spec: IndexPatternField['spec'];
customName: string;
}
export interface FieldEdiorProps {
@ -166,6 +167,7 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
isSaving: false,
format: props.indexPattern.getFormatterForField(spec),
spec: { ...spec },
customName: '',
};
this.supportedLangs = getSupportedScriptingLanguages();
this.deprecatedLangs = getDeprecatedScriptingLanguages();
@ -209,6 +211,7 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
data.fieldFormats
),
fieldFormatId: indexPattern.getFormatterForFieldNoDefault(spec.name)?.type?.id,
customName: spec.customName || '',
fieldFormatParams: format.params(),
});
}
@ -411,6 +414,33 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
);
}
renderCustomName() {
const { customName, spec } = this.state;
return (
<EuiFormRow
label={i18n.translate('indexPatternManagement.customNameLabel', {
defaultMessage: 'Custom name',
})}
helpText={
<FormattedMessage
id="indexPatternManagement.customNameHelpText"
defaultMessage="Set a custom name to use when this field is displayed in Discover and Visualize. Queries and filters don't currently support a custom name and will use the original field name."
/>
}
>
<EuiFieldText
value={customName || ''}
placeholder={spec.name}
data-test-subj="editorFieldCustomName"
onChange={(e) => {
this.setState({ customName: e.target.value });
}}
/>
</EuiFormRow>
);
}
/**
* renders a warning and a table of conflicting indices
* in case there are indices with different types
@ -772,7 +802,7 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
saveField = async () => {
const field = this.state.spec;
const { indexPattern } = this.props;
const { fieldFormatId, fieldFormatParams } = this.state;
const { fieldFormatId, fieldFormatParams, customName } = this.state;
if (field.scripted) {
this.setState({
@ -813,6 +843,11 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
indexPattern.deleteFieldFormat(field.name);
}
if (field.customName !== customName) {
field.customName = customName;
indexPattern.fields.update(field);
}
return indexPatternService
.updateSavedObject(indexPattern)
.then(() => {
@ -873,6 +908,7 @@ export class FieldEditor extends PureComponent<FieldEdiorProps, FieldEditorState
<EuiForm>
{this.renderScriptingPanels()}
{this.renderName()}
{this.renderCustomName()}
{this.renderLanguage()}
{this.renderType()}
{this.renderTypeConflict()}

View file

@ -293,6 +293,16 @@ export default function ({ getService, getPageObjects }) {
const currentUrlWithoutScore = await browser.getCurrentUrl();
expect(currentUrlWithoutScore).not.to.contain('_score');
});
it('should add a field with customLabel, sort by it, display it correctly', async function () {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.clickFieldListItemAdd('referer');
await PageObjects.discover.clickFieldSort('referer');
expect(await PageObjects.discover.getDocHeader()).to.have.string('Referer custom');
expect(await PageObjects.discover.getAllFieldNames()).to.contain('Referer custom');
const url = await browser.getCurrentUrl();
expect(url).to.contain('referer');
});
});
describe('refresh interval', function () {

View file

@ -209,6 +209,29 @@ export default function ({ getService, getPageObjects }) {
]);
});
it('should show correct data when selecting a field by its custom name', async () => {
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
await PageObjects.visEditor.clickBucket('Split rows');
await PageObjects.visEditor.selectAggregation('Date Histogram');
await PageObjects.visEditor.selectField('UTC time');
await PageObjects.visEditor.setInterval('Day');
await PageObjects.visEditor.clickGo();
const data = await PageObjects.visChart.getTableVisData();
expect(data.trim().split('\n')).to.be.eql([
'2015-09-20',
'4,757',
'2015-09-21',
'4,614',
'2015-09-22',
'4,633',
]);
const header = await PageObjects.visChart.getTableVisHeader();
expect(header).to.contain('UTC time');
});
it('should correctly filter for applied time filter on the main timefield', async () => {
await filterBar.addFilter('@timestamp', 'is between', '2015-09-19', '2015-09-21');
await PageObjects.visChart.waitForVisualizationRenderingStabilized();

File diff suppressed because one or more lines are too long

View file

@ -93,6 +93,9 @@
},
"title": {
"type": "text"
},
"fieldAttrs": {
"type": "text"
}
}
},

File diff suppressed because one or more lines are too long

View file

@ -93,6 +93,9 @@
},
"title": {
"type": "text"
},
"fieldAttrs": {
"type": "text"
}
}
},

View file

@ -246,9 +246,9 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider
public async getAllFieldNames() {
const sidebar = await testSubjects.find('discover-sidebar');
const $ = await sidebar.parseDomContent();
return $('.dscSidebar__item[attr-field]')
return $('.dscSidebarField__name')
.toArray()
.map((field) => $(field).find('span.eui-textTruncate').text());
.map((field) => $(field).text());
}
public async getSidebarWidth() {

View file

@ -328,6 +328,13 @@ export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrPr
return await testSubjects.getVisibleText('paginated-table-body');
}
/**
* This function returns the text displayed in the Table Vis header
*/
public async getTableVisHeader() {
return await testSubjects.getVisibleText('paginated-table-header');
}
/**
* This function is the newer function to retrieve data from within a table visualization.
* It uses a better return format, than the old getTableVisData, by properly splitting

View file

@ -93,6 +93,9 @@
},
"title": {
"type": "text"
},
"fieldAttrs": {
"type": "text"
}
}
},