Skip to content

Commit

Permalink
[Observability] [Exploratory View] add percentile ranks, show legend …
Browse files Browse the repository at this point in the history
…always, and fix field labels (#113765) (#114848)

* add percentile ranks, show legend always, and fix field labels

* add 50th percentile

* replace hard coded values with constant

Co-authored-by: Kibana Machine <[email protected]>
# Conflicts:
#	x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.test.tsx
  • Loading branch information
dominiqueclarke authored Oct 13, 2021
1 parent b04bf42 commit dd68f7e
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { OperationType } from '../../../../../../../lens/public';
import { ReportViewType } from '../../types';
import {
CLS_FIELD,
Expand All @@ -13,6 +13,7 @@ import {
LCP_FIELD,
TBT_FIELD,
TRANSACTION_TIME_TO_FIRST_BYTE,
TRANSACTION_DURATION,
} from './elasticsearch_fieldnames';
import {
AGENT_HOST_LABEL,
Expand Down Expand Up @@ -45,6 +46,8 @@ import {
TBT_LABEL,
URL_LABEL,
BACKEND_TIME_LABEL,
MONITORS_DURATION_LABEL,
PAGE_LOAD_TIME_LABEL,
LABELS_FIELD,
} from './labels';

Expand All @@ -69,9 +72,11 @@ export const FieldLabels: Record<string, string> = {
[FID_FIELD]: FID_LABEL,
[CLS_FIELD]: CLS_LABEL,
[TRANSACTION_TIME_TO_FIRST_BYTE]: BACKEND_TIME_LABEL,
[TRANSACTION_DURATION]: PAGE_LOAD_TIME_LABEL,

'monitor.id': MONITOR_ID_LABEL,
'monitor.status': MONITOR_STATUS_LABEL,
'monitor.duration.us': MONITORS_DURATION_LABEL,

'agent.hostname': AGENT_HOST_LABEL,
'host.hostname': HOST_NAME_LABEL,
Expand All @@ -86,6 +91,7 @@ export const FieldLabels: Record<string, string> = {
'performance.metric': METRIC_LABEL,
'Business.KPI': KPI_LABEL,
'http.request.method': REQUEST_METHOD,
percentile: 'Percentile',
LABEL_FIELDS_FILTER: LABELS_FIELD,
LABEL_FIELDS_BREAKDOWN: 'Labels field',
};
Expand Down Expand Up @@ -114,8 +120,16 @@ export const USE_BREAK_DOWN_COLUMN = 'USE_BREAK_DOWN_COLUMN';
export const FILTER_RECORDS = 'FILTER_RECORDS';
export const TERMS_COLUMN = 'TERMS_COLUMN';
export const OPERATION_COLUMN = 'operation';
export const PERCENTILE = 'percentile';

export const REPORT_METRIC_FIELD = 'REPORT_METRIC_FIELD';

export const PERCENTILE_RANKS = [
'99th' as OperationType,
'95th' as OperationType,
'90th' as OperationType,
'75th' as OperationType,
'50th' as OperationType,
];
export const LABEL_FIELDS_FILTER = 'LABEL_FIELDS_FILTER';
export const LABEL_FIELDS_BREAKDOWN = 'LABEL_FIELDS_BREAKDOWN';
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from './constants/elasticsearch_fieldnames';
import { buildExistsFilter, buildPhrasesFilter } from './utils';
import { sampleAttributeKpi } from './test_data/sample_attribute_kpi';
import { RECORDS_FIELD, REPORT_METRIC_FIELD, ReportTypes } from './constants';
import { RECORDS_FIELD, REPORT_METRIC_FIELD, PERCENTILE_RANKS, ReportTypes } from './constants';

describe('Lens Attribute', () => {
mockAppIndexPattern();
Expand Down Expand Up @@ -75,6 +75,63 @@ describe('Lens Attribute', () => {
expect(lnsAttrKpi.getJSON()).toEqual(sampleAttributeKpi);
});

it('should return expected json for percentile breakdowns', function () {
const seriesConfigKpi = getDefaultConfigs({
reportType: ReportTypes.KPI,
dataType: 'ux',
indexPattern: mockIndexPattern,
});

const lnsAttrKpi = new LensAttributes([
{
filters: [],
seriesConfig: seriesConfigKpi,
time: {
from: 'now-1h',
to: 'now',
},
indexPattern: mockIndexPattern,
name: 'ux-series-1',
breakdown: 'percentile',
reportDefinitions: {},
selectedMetricField: 'transaction.duration.us',
color: '#54b399',
},
]);

expect(lnsAttrKpi.getJSON().state.datasourceStates.indexpattern.layers.layer0.columns).toEqual({
'x-axis-column-layer0': {
dataType: 'date',
isBucketed: true,
label: '@timestamp',
operationType: 'date_histogram',
params: {
interval: 'auto',
},
scale: 'interval',
sourceField: '@timestamp',
},
...PERCENTILE_RANKS.reduce((acc: Record<string, any>, rank, index) => {
acc[`y-axis-column-${index === 0 ? 'layer' + index : index}`] = {
dataType: 'number',
filter: {
language: 'kuery',
query: 'transaction.type: page-load and processor.event: transaction',
},
isBucketed: false,
label: `${rank} percentile of page load time`,
operationType: 'percentile',
params: {
percentile: Number(rank.slice(0, 2)),
},
scale: 'ratio',
sourceField: 'transaction.duration.us',
};
return acc;
}, {}),
});
});

it('should return main y axis', function () {
expect(lnsAttr.getMainYAxis(layerConfig, 'layer0', '')).toEqual({
dataType: 'number',
Expand Down Expand Up @@ -413,7 +470,7 @@ describe('Lens Attribute', () => {
yConfig: [{ color: 'green', forAccessor: 'y-axis-column-layer0' }],
},
],
legend: { isVisible: true, position: 'right' },
legend: { isVisible: true, showSingleSeries: true, position: 'right' },
preferredSeriesType: 'line',
tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true },
valueLabels: 'hide',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import {
REPORT_METRIC_FIELD,
RECORDS_FIELD,
RECORDS_PERCENTAGE_FIELD,
PERCENTILE,
PERCENTILE_RANKS,
ReportTypes,
} from './constants';
import { ColumnFilter, SeriesConfig, UrlFilter, URLReportDefinition } from '../types';
Expand Down Expand Up @@ -249,6 +251,30 @@ export class LensAttributes {
};
}

getPercentileBreakdowns(
layerConfig: LayerConfig,
columnFilter?: string
): Record<string, FieldBasedIndexPatternColumn> {
const yAxisColumns = layerConfig.seriesConfig.yAxisColumns;
const { sourceField: mainSourceField, label: mainLabel } = yAxisColumns[0];
const lensColumns: Record<string, FieldBasedIndexPatternColumn> = {};

// start at 1, because main y axis will have the first percentile breakdown
for (let i = 1; i < PERCENTILE_RANKS.length; i++) {
lensColumns[`y-axis-column-${i}`] = {
...this.getColumnBasedOnType({
sourceField: mainSourceField!,
operationType: PERCENTILE_RANKS[i],
label: mainLabel,
layerConfig,
colIndex: i,
}),
filter: { query: columnFilter || '', language: 'kuery' },
};
}
return lensColumns;
}

getPercentileNumberColumn(
sourceField: string,
percentileValue: string,
Expand All @@ -258,7 +284,7 @@ export class LensAttributes {
...buildNumberColumn(sourceField),
label: i18n.translate('xpack.observability.expView.columns.label', {
defaultMessage: '{percentileValue} percentile of {sourceField}',
values: { sourceField: seriesConfig.labels[sourceField], percentileValue },
values: { sourceField: seriesConfig.labels[sourceField]?.toLowerCase(), percentileValue },
}),
operationType: 'percentile',
params: { percentile: Number(percentileValue.split('th')[0]) },
Expand Down Expand Up @@ -328,6 +354,7 @@ export class LensAttributes {
layerConfig: LayerConfig;
colIndex?: number;
}) {
const { breakdown, seriesConfig } = layerConfig;
const { fieldMeta, columnType, fieldName, columnLabel, timeScale, columnFilters } =
this.getFieldMeta(sourceField, layerConfig);

Expand All @@ -348,6 +375,18 @@ export class LensAttributes {
if (fieldType === 'date') {
return this.getDateHistogramColumn(fieldName);
}

if (fieldType === 'number' && breakdown === PERCENTILE) {
return {
...this.getPercentileNumberColumn(
fieldName,
operationType || PERCENTILE_RANKS[0],
seriesConfig!
),
filter: colIndex !== undefined ? columnFilters?.[colIndex] : undefined,
};
}

if (fieldType === 'number') {
return this.getNumberColumn({
sourceField: fieldName,
Expand Down Expand Up @@ -395,6 +434,7 @@ export class LensAttributes {
}

getMainYAxis(layerConfig: LayerConfig, layerId: string, columnFilter: string) {
const { breakdown } = layerConfig;
const { sourceField, operationType, label } = layerConfig.seriesConfig.yAxisColumns[0];

if (sourceField === RECORDS_PERCENTAGE_FIELD) {
Expand All @@ -407,14 +447,15 @@ export class LensAttributes {

return this.getColumnBasedOnType({
sourceField,
operationType,
operationType: breakdown === PERCENTILE ? PERCENTILE_RANKS[0] : operationType,
label,
layerConfig,
colIndex: 0,
});
}

getChildYAxises(layerConfig: LayerConfig, layerId?: string, columnFilter?: string) {
const { breakdown } = layerConfig;
const lensColumns: Record<string, FieldBasedIndexPatternColumn | SumIndexPatternColumn> = {};
const yAxisColumns = layerConfig.seriesConfig.yAxisColumns;
const { sourceField: mainSourceField, label: mainLabel } = yAxisColumns[0];
Expand All @@ -424,7 +465,10 @@ export class LensAttributes {
.supportingColumns;
}

// 1 means there is only main y axis
if (yAxisColumns.length === 1 && breakdown === PERCENTILE) {
return this.getPercentileBreakdowns(layerConfig, columnFilter);
}

if (yAxisColumns.length === 1) {
return lensColumns;
}
Expand Down Expand Up @@ -574,7 +618,7 @@ export class LensAttributes {
layers[layerId] = {
columnOrder: [
`x-axis-column-${layerId}`,
...(breakdown && sourceField !== USE_BREAK_DOWN_COLUMN
...(breakdown && sourceField !== USE_BREAK_DOWN_COLUMN && breakdown !== PERCENTILE
? [`breakdown-column-${layerId}`]
: []),
`y-axis-column-${layerId}`,
Expand All @@ -588,7 +632,7 @@ export class LensAttributes {
filter: { query: columnFilter, language: 'kuery' },
...(timeShift ? { timeShift } : {}),
},
...(breakdown && sourceField !== USE_BREAK_DOWN_COLUMN
...(breakdown && sourceField !== USE_BREAK_DOWN_COLUMN && breakdown !== PERCENTILE
? // do nothing since this will be used a x axis source
{
[`breakdown-column-${layerId}`]: this.getBreakdownColumn({
Expand All @@ -610,7 +654,7 @@ export class LensAttributes {

getXyState(): XYState {
return {
legend: { isVisible: true, position: 'right' },
legend: { isVisible: true, showSingleSeries: true, position: 'right' },
valueLabels: 'hide',
fittingFunction: 'Linear',
curveType: 'CURVE_MONOTONE_X' as XYCurveType,
Expand All @@ -636,6 +680,7 @@ export class LensAttributes {
],
xAccessor: `x-axis-column-layer${index}`,
...(layerConfig.breakdown &&
layerConfig.breakdown !== PERCENTILE &&
layerConfig.seriesConfig.xAxisColumn.sourceField !== USE_BREAK_DOWN_COLUMN
? { splitAccessor: `breakdown-column-layer${index}` }
: {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
OPERATION_COLUMN,
RECORDS_FIELD,
REPORT_METRIC_FIELD,
PERCENTILE,
ReportTypes,
} from '../constants';
import { buildPhraseFilter } from '../utils';
Expand Down Expand Up @@ -81,6 +82,7 @@ export function getKPITrendsLensConfig({ indexPattern }: ConfigProps): SeriesCon
USER_AGENT_OS,
CLIENT_GEO_COUNTRY_NAME,
USER_AGENT_DEVICE,
PERCENTILE,
LABEL_FIELDS_BREAKDOWN,
],
baseFilters: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
*/

import { ConfigProps, SeriesConfig } from '../../types';
import { FieldLabels, OPERATION_COLUMN, REPORT_METRIC_FIELD, ReportTypes } from '../constants';
import {
FieldLabels,
OPERATION_COLUMN,
REPORT_METRIC_FIELD,
PERCENTILE,
ReportTypes,
} from '../constants';
import {
CLS_LABEL,
DCL_LABEL,
Expand Down Expand Up @@ -44,7 +50,7 @@ export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesCon
],
hasOperationType: false,
filterFields: ['observer.geo.name', 'monitor.type', 'tags'],
breakdownFields: ['observer.geo.name', 'monitor.type', 'monitor.name'],
breakdownFields: ['observer.geo.name', 'monitor.type', 'monitor.name', PERCENTILE],
baseFilters: [],
palette: { type: 'palette', name: 'status' },
definitionFields: ['monitor.name', 'url.full'],
Expand Down Expand Up @@ -98,6 +104,6 @@ export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesCon
columnType: OPERATION_COLUMN,
},
],
labels: { ...FieldLabels },
labels: { ...FieldLabels, [SUMMARY_UP]: UP_LABEL, [SUMMARY_DOWN]: DOWN_LABEL },
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export const sampleAttribute = {
],
legend: {
isVisible: true,
showSingleSeries: true,
position: 'right',
},
preferredSeriesType: 'line',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const sampleAttributeCoreWebVital = {
],
legend: {
isVisible: true,
showSingleSeries: true,
position: 'right',
},
preferredSeriesType: 'line',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const sampleAttributeKpi = {
],
legend: {
isVisible: true,
showSingleSeries: true,
position: 'right',
},
preferredSeriesType: 'line',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { fireEvent, screen } from '@testing-library/react';
import { Breakdowns } from './breakdowns';
import { mockIndexPattern, mockUxSeries, render } from '../../rtl_helpers';
import { getDefaultConfigs } from '../../configurations/default_configs';
import { RECORDS_FIELD } from '../../configurations/constants';
import { USER_AGENT_OS } from '../../configurations/constants/elasticsearch_fieldnames';

describe('Breakdowns', function () {
Expand Down Expand Up @@ -56,6 +57,26 @@ describe('Breakdowns', function () {
expect(setSeries).toHaveBeenCalledTimes(1);
});

it('does not show percentile breakdown for records metrics', function () {
const kpiConfig = getDefaultConfigs({
reportType: 'kpi-over-time',
indexPattern: mockIndexPattern,
dataType: 'ux',
});

render(
<Breakdowns
seriesId={0}
seriesConfig={kpiConfig}
series={{ ...mockUxSeries, selectedMetricField: RECORDS_FIELD }}
/>
);

fireEvent.click(screen.getByTestId('seriesBreakdown'));

expect(screen.queryByText('Percentile')).not.toBeInTheDocument();
});

it('should disable breakdowns when a different series has a breakdown', function () {
const initSeries = {
data: [mockUxSeries, { ...mockUxSeries, breakdown: undefined }],
Expand Down
Loading

0 comments on commit dd68f7e

Please sign in to comment.