diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/utils.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/utils.ts index 6418023586084..9c6ea500c97c7 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/utils.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/utils.ts @@ -28,7 +28,7 @@ export type DateHistogramColumnParams = DateHistogramIndexPatternColumn['params' export type TopValuesColumnParams = Pick< TermsIndexPatternColumn['params'], - 'size' | 'orderDirection' | 'orderBy' | 'secondaryFields' | 'accuracyMode' + 'size' | 'orderDirection' | 'orderBy' | 'secondaryFields' | 'accuracyMode' | 'orderAgg' >; export const getHistogramColumn = ({ diff --git a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.test.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.test.ts index 044b57c64da28..a4825c11f85cd 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.test.ts @@ -17,7 +17,11 @@ const useCases = [ filter: '', name: '', }, - 'sum(system.cpu.user.pct)', + { + operation: 'sum', + operationWithField: 'sum(system.cpu.user.pct)', + sourceField: 'system.cpu.user.pct', + }, ], [ { @@ -26,7 +30,11 @@ const useCases = [ filter: '', name: '', }, - 'max(system.cpu.user.pct)', + { + operation: 'max', + operationWithField: 'max(system.cpu.user.pct)', + sourceField: 'system.cpu.user.pct', + }, ], [ { @@ -35,7 +43,11 @@ const useCases = [ filter: '', name: '', }, - 'min(system.cpu.user.pct)', + { + operation: 'min', + operationWithField: 'min(system.cpu.user.pct)', + sourceField: 'system.cpu.user.pct', + }, ], [ { @@ -44,16 +56,24 @@ const useCases = [ filter: '', name: '', }, - 'average(system.cpu.user.pct)', + { + operation: 'average', + operationWithField: 'average(system.cpu.user.pct)', + sourceField: 'system.cpu.user.pct', + }, ], [ { aggType: Aggregators.COUNT, - field: 'system.cpu.user.pct', - filter: '', + field: '', + filter: 'system.cpu.user.pct: *', name: '', }, - 'count(___records___)', + { + operation: 'count', + operationWithField: `count(kql='system.cpu.user.pct: *')`, + sourceField: '', + }, ], [ { @@ -62,7 +82,11 @@ const useCases = [ filter: '', name: '', }, - 'unique_count(system.cpu.user.pct)', + { + operation: 'unique_count', + operationWithField: 'unique_count(system.cpu.user.pct)', + sourceField: 'system.cpu.user.pct', + }, ], [ { @@ -71,7 +95,11 @@ const useCases = [ filter: '', name: '', }, - 'percentile(system.cpu.user.pct, percentile=95)', + { + operation: 'percentile', + operationWithField: 'percentile(system.cpu.user.pct, percentile=95)', + sourceField: 'system.cpu.user.pct', + }, ], [ { @@ -80,7 +108,11 @@ const useCases = [ filter: '', name: '', }, - 'percentile(system.cpu.user.pct, percentile=99)', + { + operation: 'percentile', + operationWithField: 'percentile(system.cpu.user.pct, percentile=99)', + sourceField: 'system.cpu.user.pct', + }, ], [ { @@ -89,7 +121,11 @@ const useCases = [ filter: '', name: '', }, - `counter_rate(max(system.network.in.bytes), kql='')`, + { + operation: 'counter_rate', + operationWithField: `counter_rate(max(system.network.in.bytes), kql='')`, + sourceField: 'system.network.in.bytes', + }, ], [ { @@ -98,7 +134,11 @@ const useCases = [ filter: 'host.name : "foo"', name: '', }, - `counter_rate(max(system.network.in.bytes), kql='host.name : foo')`, + { + operation: 'counter_rate', + operationWithField: `counter_rate(max(system.network.in.bytes), kql='host.name : foo')`, + sourceField: 'system.network.in.bytes', + }, ], ]; diff --git a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.ts index a487f4899ad06..9eb52af738ea9 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.ts @@ -8,14 +8,24 @@ import { Aggregators } from '../../../common/custom_threshold_rule/types'; import { GenericMetric } from './rule_condition_chart'; -export const getLensOperationFromRuleMetric = (metric: GenericMetric): string => { +export interface LensOperation { + operation: string; + operationWithField: string; + sourceField: string; +} + +export const getLensOperationFromRuleMetric = (metric: GenericMetric): LensOperation => { const { aggType, field, filter } = metric; let operation: string = aggType; const operationArgs: string[] = []; const aggFilter = JSON.stringify(filter || '').replace(/"|\\/g, ''); if (aggType === Aggregators.RATE) { - return `counter_rate(max(${field}), kql='${aggFilter}')`; + return { + operation: 'counter_rate', + operationWithField: `counter_rate(max(${field}), kql='${aggFilter}')`, + sourceField: field || '', + }; } if (aggType === Aggregators.AVERAGE) operation = 'average'; @@ -23,14 +33,10 @@ export const getLensOperationFromRuleMetric = (metric: GenericMetric): string => if (aggType === Aggregators.P95 || aggType === Aggregators.P99) operation = 'percentile'; if (aggType === Aggregators.COUNT) operation = 'count'; - let sourceField = field; - - if (aggType === Aggregators.COUNT) { - sourceField = '___records___'; + if (field) { + operationArgs.push(field); } - operationArgs.push(sourceField || ''); - if (aggType === Aggregators.P95) { operationArgs.push('percentile=95'); } @@ -41,7 +47,11 @@ export const getLensOperationFromRuleMetric = (metric: GenericMetric): string => if (aggFilter) operationArgs.push(`kql='${aggFilter}'`); - return operation + '(' + operationArgs.join(', ') + ')'; + return { + operation, + operationWithField: `${operation}(${operationArgs.join(', ')})`, + sourceField: field || '', + }; }; export const getBufferThreshold = (threshold?: number): string => diff --git a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.test.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.test.ts index a957c58167403..647791cab5b8d 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.test.ts @@ -17,7 +17,11 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -28,7 +32,11 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - 'ABC-abc': 'average(system.cpu.system.pct)', + 'ABC-abc': { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, }, }); expect(parser.parse()).toEqual('100*average(system.cpu.system.pct)'); @@ -38,8 +46,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -52,9 +68,21 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', - C: 'average(system.cpu.cores)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, + C: { + operationWithField: 'average(system.cpu.cores)', + operation: 'average', + sourceField: 'system.cpu.cores', + }, }, }); // ✅ checked with Lens Formula editor @@ -67,7 +95,11 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -79,8 +111,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -93,8 +133,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -107,8 +155,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -121,8 +177,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -135,8 +199,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -149,8 +221,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -163,8 +243,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -178,8 +266,16 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - aa: 'average(system.cpu.system.pct)', - baa: 'average(system.cpu.user.pct)', + aa: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + baa: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, }, }); expect(parser.parse()).toEqual( @@ -193,12 +289,36 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', - C: 'average(system.cpu.total.pct)', - D: 'average(system.cpu.cores)', - E: 'count()', - F: 'sum(system.cpu.total.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, + C: { + operationWithField: 'average(system.cpu.total.pct)', + operation: 'average', + sourceField: 'system.cpu.total.pct', + }, + D: { + operationWithField: 'average(system.cpu.cores)', + operation: 'average', + sourceField: 'system.cpu.cores', + }, + E: { + operationWithField: 'count()', + operation: 'count', + sourceField: '', + }, + F: { + operationWithField: 'sum(system.cpu.total.pct)', + operation: 'sum', + sourceField: 'system.cpu.total.pct', + }, }, }); // ✅ checked with Lens Formula editor @@ -213,12 +333,36 @@ describe('PainlessTinyMathParser', () => { const parser = new PainlessTinyMathParser({ equation, aggMap: { - A: 'average(system.cpu.system.pct)', - B: 'average(system.cpu.user.pct)', - C: 'average(system.cpu.total.pct)', - D: 'average(system.cpu.cores)', - E: 'count()', - F: 'sum(system.cpu.total.pct)', + A: { + operationWithField: 'average(system.cpu.system.pct)', + operation: 'average', + sourceField: 'system.cpu.system.pct', + }, + B: { + operationWithField: 'average(system.cpu.user.pct)', + operation: 'average', + sourceField: 'system.cpu.user.pct', + }, + C: { + operationWithField: 'average(system.cpu.total.pct)', + operation: 'average', + sourceField: 'system.cpu.total.pct', + }, + D: { + operationWithField: 'average(system.cpu.cores)', + operation: 'average', + sourceField: 'system.cpu.cores', + }, + E: { + operationWithField: 'count()', + operation: 'count', + sourceField: '', + }, + F: { + operationWithField: 'sum(system.cpu.total.pct)', + operation: 'sum', + sourceField: 'system.cpu.total.pct', + }, }, }); // ✅ checked with Lens Formula editor diff --git a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.ts index 25ec15d3a808b..8c09ba029c579 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { LensOperation } from './helpers'; + // This is a parser of a subset operations/expression/statement of Painless A-Z, +, -, /, *, (, ), ?, !, &, :, |, >, <, = to be used in Lens formula editor that uses TinyMath // The goal is to parse painless expressions to a format that can be used in Lens formula editor // The parser will also replace the characters A-Z with the values from aggMap @@ -13,7 +15,7 @@ // This parser is using a simple recursive function to parse the expression and replace the characters with the values from aggMap export interface AggMap { - [key: string]: string; + [key: string]: LensOperation; } interface PainlessTinyMathParserProps { equation: string; @@ -81,7 +83,10 @@ export class PainlessTinyMathParser { .sort() .reverse() .forEach((metricName) => { - parsedInputString = parsedInputString.replaceAll(metricName, aggMap[metricName]); + parsedInputString = parsedInputString.replaceAll( + metricName, + aggMap[metricName].operationWithField + ); }); return parsedInputString; diff --git a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx index 4567d7c37b10b..2a9fa2c295274 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx @@ -8,7 +8,7 @@ import React, { useState, useEffect } from 'react'; import { EuiEmptyPrompt, useEuiTheme } from '@elastic/eui'; import { Query, Filter } from '@kbn/es-query'; -import { FillStyle, SeriesType } from '@kbn/lens-plugin/public'; +import { FillStyle, SeriesType, TermsIndexPatternColumn } from '@kbn/lens-plugin/public'; import { DataView } from '@kbn/data-views-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import useAsync from 'react-use/lib/useAsync'; @@ -82,6 +82,10 @@ export interface RuleConditionChartProps { additionalFilters?: Filter[]; } +export type TopValuesOrderParams = + | Pick + | undefined; + const defaultQuery: Query = { language: 'kuery', query: '', @@ -299,10 +303,10 @@ export function RuleConditionChart({ return; } const aggMapFromMetrics = metrics.reduce((acc, metric) => { - const operationField = getLensOperationFromRuleMetric(metric); + const { operation, operationWithField, sourceField } = getLensOperationFromRuleMetric(metric); return { ...acc, - [metric.name]: operationField, + [metric.name]: { operation, operationWithField, sourceField }, }; }, {} as AggMap); @@ -354,6 +358,26 @@ export function RuleConditionChart({ seriesType: seriesType ? seriesType : 'bar', }; + const firstMetricAggMap = aggMap && metrics.length > 0 ? aggMap[metrics[0].name] : undefined; + const convertToMaxOperation = ['counter_rate', 'last_value', 'percentile']; + + const orderParams: TopValuesOrderParams = firstMetricAggMap + ? { + orderDirection: 'desc', + orderBy: { type: 'custom' }, + orderAgg: { + label: firstMetricAggMap.operationWithField, + dataType: 'number', + operationType: convertToMaxOperation.includes(firstMetricAggMap.operation) + ? 'max' + : firstMetricAggMap.operation, + sourceField: firstMetricAggMap.sourceField, + isBucketed: false, + scale: 'ratio', + }, + } + : undefined; + if (groupBy && groupBy?.length) { xYDataLayerOptions.breakdown = { type: 'top_values', @@ -362,6 +386,7 @@ export function RuleConditionChart({ size: 3, secondaryFields: (groupBy as string[]).slice(1), accuracyMode: false, + ...orderParams, }, }; } @@ -425,6 +450,7 @@ export function RuleConditionChart({ timeUnit, seriesType, warningThresholdReferenceLine, + aggMap, ]); if (