[TSVB][Lens] Added support of Variance aggregation for navigate to lens (#143209)

* Added support of variance for navigate to lens

* Fixed types

* Fixed type

* Fix variance for moving average and derivative

Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Uladzislau Lasitsa 2022-10-14 13:48:41 +03:00 committed by GitHub
parent 3cb5fedca7
commit 3cf9778127
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 123 additions and 2 deletions

View file

@ -31,5 +31,6 @@ export { convertToDateHistogramColumn } from './date_histogram';
export { convertToTermsColumn } from './terms';
export { convertToCounterRateColumn } from './counter_rate';
export { convertToStandartDeviationColumn } from './std_deviation';
export { convertVarianceToFormulaColumn } from './variance';
export * from './types';

View file

@ -237,7 +237,11 @@ const convertMovingAvgOrDerivativeToColumns = (
const [nestedFieldId, _] = subMetricField?.split('[') ?? [];
// support nested aggs with formula
const additionalSubFunction = metrics.find(({ id }) => id === nestedFieldId);
if (additionalSubFunction || pipelineAgg.name === 'counter_rate') {
if (
additionalSubFunction ||
pipelineAgg.name === 'counter_rate' ||
subFunctionMetric.type === 'variance'
) {
const formula = getPipelineSeriesFormula(metric, metrics, subFunctionMetric, {
metaValue,
reducedTimeRange,

View file

@ -0,0 +1,47 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { createSeries } from '../__mocks__';
import { FormulaColumn } from './types';
import { Metric } from '../../../../common/types';
import { TSVB_METRIC_TYPES } from '../../../../common/enums';
import { convertVarianceToFormulaColumn } from './variance';
describe('convertVarianceToFormulaColumn', () => {
const series = createSeries();
const dataView = stubLogstashDataView;
const metric: Metric = {
id: 'some-id',
type: TSVB_METRIC_TYPES.VARIANCE,
};
const field = dataView.fields[0].name;
test.each<
[string, Parameters<typeof convertVarianceToFormulaColumn>, Partial<FormulaColumn> | null]
>([
['null if field is not provided', [{ series, metrics: [metric], dataView }], null],
[
'correct formula column',
[{ series, metrics: [{ ...metric, field }], dataView }],
{
meta: { metricId: 'some-id' },
operationType: 'formula',
params: {
formula: 'pow(standard_deviation(bytes), 2)',
},
},
],
])('should return %s', (_, input, expected) => {
if (expected === null) {
expect(convertVarianceToFormulaColumn(...input)).toBeNull();
} else {
expect(convertVarianceToFormulaColumn(...input)).toEqual(expect.objectContaining(expected));
}
});
});

View file

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { getFormulaEquivalent } from '../metrics';
import { createFormulaColumn } from './formula';
import { CommonColumnsConverterArgs } from './types';
export const convertVarianceToFormulaColumn = (
{ series, metrics, dataView }: CommonColumnsConverterArgs,
reducedTimeRange?: string
) => {
const metric = metrics[metrics.length - 1];
const field = metric.field ? dataView.getFieldByName(metric.field) : undefined;
if (!field) {
return null;
}
const script = getFormulaEquivalent(metric, metrics, {
reducedTimeRange,
timeShift: series.offset_time,
});
if (!script) return null;
return createFormulaColumn(script, { series, metric, dataView });
};

View file

@ -62,6 +62,12 @@ describe('getFormulaEquivalent', () => {
mode: 'upper',
};
const variance: Metric = {
id: 'some-random-value',
type: TSVB_METRIC_TYPES.VARIANCE,
field: 'test-1',
};
const sibblingPipelineMetric: Metric[] = [
{
id: 'test-1',
@ -141,6 +147,11 @@ describe('getFormulaEquivalent', () => {
[stdDeviationMetricWithUpperMode, [stdDeviationMetricWithUpperMode], {}],
'average(test-1) + 1.5 * standard_deviation(test-1)',
],
[
'correct formula if metric is variance',
[variance, [variance], {}],
'pow(standard_deviation(test-1), 2)',
],
[
'correct formula if metric is supported',
[supportedMetric, [supportedMetric], {}],

View file

@ -144,6 +144,12 @@ export const getFormulaEquivalent = (
case 'static': {
return `${currentMetric.value}`;
}
case 'variance': {
return `${aggFormula}(standard_deviation(${currentMetric.field}${addAdditionalArgs({
reducedTimeRange,
timeShift,
})}), 2)`;
}
case 'std_deviation': {
if (currentMetric.mode === 'lower') {
return `average(${currentMetric.field}${addAdditionalArgs({

View file

@ -55,6 +55,7 @@ interface LocalSupportedMetrics {
[TSVB_METRIC_TYPES.STATIC]: Metric<typeof Operations.STATIC_VALUE>;
[TSVB_METRIC_TYPES.POSITIVE_RATE]: Metric<typeof Operations.COUNTER_RATE>;
[TSVB_METRIC_TYPES.MOVING_AVERAGE]: Metric<typeof Operations.MOVING_AVERAGE>;
[TSVB_METRIC_TYPES.VARIANCE]: Metric<typeof Operations.FORMULA>;
}
type UnsupportedSupportedMetrics = Exclude<MetricType, keyof LocalSupportedMetrics>;
@ -250,6 +251,15 @@ export const SUPPORTED_METRICS: SupportedMetrics = {
supportedPanelTypes,
supportedTimeRangeModes,
},
variance: {
name: 'formula',
isFormula: true,
formula: 'pow',
isFullReference: false,
isFieldRequired: true,
supportedPanelTypes,
supportedTimeRangeModes,
},
} as const;
type SupportedMetricsKeys = keyof LocalSupportedMetrics;

View file

@ -23,6 +23,7 @@ const mockConvertToStaticValueColumn = jest.fn();
const mockConvertStaticValueToFormulaColumn = jest.fn();
const mockConvertToStandartDeviationColumn = jest.fn();
const mockConvertMetricAggregationColumnWithoutSpecialParams = jest.fn();
const mockConvertVarianceToFormulaColumn = jest.fn();
jest.mock('../convert', () => ({
convertMathToFormulaColumn: jest.fn(() => mockConvertMathToFormulaColumn()),
@ -38,6 +39,7 @@ jest.mock('../convert', () => ({
convertMetricAggregationColumnWithoutSpecialParams: jest.fn(() =>
mockConvertMetricAggregationColumnWithoutSpecialParams()
),
convertVarianceToFormulaColumn: jest.fn(() => mockConvertVarianceToFormulaColumn()),
}));
describe('getMetricsColumns', () => {
@ -163,6 +165,11 @@ describe('getMetricsColumns', () => {
],
mockConvertToStandartDeviationColumn,
],
[
'call convertVarianceToFormulaColumn if metric type is variance',
[createSeries({ metrics: [{ type: TSVB_METRIC_TYPES.VARIANCE, id: '1' }] }), dataView, 1],
mockConvertVarianceToFormulaColumn,
],
[
'call convertMetricAggregationColumnWithoutSpecialParams if metric type is another supported type',
[createSeries({ metrics: [{ type: METRIC_TYPES.AVG, id: '1' }] }), dataView, 1],

View file

@ -25,6 +25,7 @@ import {
convertMetricAggregationColumnWithoutSpecialParams,
convertToCounterRateColumn,
convertToStandartDeviationColumn,
convertVarianceToFormulaColumn,
} from '../convert';
import { getValidColumns } from './columns';
@ -120,6 +121,10 @@ export const getMetricsColumns = (
const column = convertToLastValueColumn(columnsConverterArgs, reducedTimeRange);
return getValidColumns(column);
}
case 'variance': {
const column = convertVarianceToFormulaColumn(columnsConverterArgs, reducedTimeRange);
return getValidColumns(column);
}
case 'static': {
const column = isStaticValueColumnSupported
? convertToStaticValueColumn(columnsConverterArgs, {

View file

@ -144,7 +144,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('should not allow converting of unsupported aggregations', async () => {
await visualBuilder.selectAggType('Variance');
await visualBuilder.selectAggType('Sum of Squares');
await visualBuilder.setFieldForAggregation('machine.ram');
await header.waitUntilLoadingHasFinished();