diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts index 044d166ca5efa..05adc2355ef3c 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts @@ -6,6 +6,7 @@ */ import { each, find, get, filter } from 'lodash'; +import type { ES_AGGREGATION } from '@kbn/ml-anomaly-utils'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -15,7 +16,6 @@ import { isModelPlotChartableForDetector, isModelPlotEnabled, } from '../../../common/util/job_utils'; -// @ts-ignore import { buildConfigFromDetector } from '../util/chart_config_builder'; import { mlResultsService } from '../services/results_service'; import { ModelPlotOutput } from '../services/results_service/result_service_rx'; @@ -113,29 +113,48 @@ function getMetricData( } } -// Builds chart detail information (charting function description and entity counts) used -// in the title area of the time series chart. -// Queries Elasticsearch if necessary to obtain the distinct count of entities -// for which data is being plotted. +interface TimeSeriesExplorerChartDetails { + success: boolean; + results: { + functionLabel: string | null; + entityData: { count?: number; entities: Array<{ fieldName: string; cardinality?: number }> }; + }; +} + +/** + * Builds chart detail information (charting function description and entity counts) used + * in the title area of the time series chart. + * Queries Elasticsearch if necessary to obtain the distinct count of entities + * for which data is being plotted. + * @param job Job config info + * @param detectorIndex The index of the detector in the job config + * @param entityFields Array of field name - field value pairs + * @param earliestMs Earliest timestamp in milliseconds + * @param latestMs Latest timestamp in milliseconds + * @param metricFunctionDescription The underlying function (min, max, avg) for "metric" detector type + * @returns chart data to plot for Single Metric Viewer/Time series explorer + */ function getChartDetails( job: Job, detectorIndex: number, - entityFields: any[], + entityFields: MlEntityField[], earliestMs: number, - latestMs: number + latestMs: number, + metricFunctionDescription?: ES_AGGREGATION ) { return new Promise((resolve, reject) => { - const obj: any = { + const obj: TimeSeriesExplorerChartDetails = { success: true, results: { functionLabel: '', entityData: { entities: [] } }, }; - const chartConfig = buildConfigFromDetector(job, detectorIndex); + const chartConfig = buildConfigFromDetector(job, detectorIndex, metricFunctionDescription); + let functionLabel: string | null = chartConfig.metricFunction; if (chartConfig.metricFieldName !== undefined) { - functionLabel += ' '; - functionLabel += chartConfig.metricFieldName; + functionLabel += ` ${chartConfig.metricFieldName}`; } + obj.results.functionLabel = functionLabel; const blankEntityFields = filter(entityFields, (entity) => { diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index ad3f71e5df22d..76c0ffb038971 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -568,7 +568,8 @@ export class TimeSeriesExplorer extends React.Component { detectorIndex, entityControls, searchBounds.min.valueOf(), - searchBounds.max.valueOf() + searchBounds.max.valueOf(), + this.props.functionDescription ) .then((resp) => { stateUpdate.chartDetails = resp.results; diff --git a/x-pack/plugins/ml/public/application/util/chart_config_builder.ts b/x-pack/plugins/ml/public/application/util/chart_config_builder.ts index e9c322d30a6e5..c1057fc335e82 100644 --- a/x-pack/plugins/ml/public/application/util/chart_config_builder.ts +++ b/x-pack/plugins/ml/public/application/util/chart_config_builder.ts @@ -19,9 +19,19 @@ import { Job } from '../../../common/types/anomaly_detection_jobs'; import { mlFunctionToESAggregation } from '../../../common/util/job_utils'; -// Builds the basic configuration to plot a chart of the source data -// analyzed by the the detector at the given index from the specified ML job. -export function buildConfigFromDetector(job: Job, detectorIndex: number) { +/** + * Builds the basic configuration to plot a chart of the source data + * analyzed by the the detector at the given index from the specified ML job. + * @param job Job config info + * @param detectorIndex The index of the detector in the job config + * @param metricFunctionDescription The underlying function (min, max, avg) for "metric" detector type + * @returns + */ +export function buildConfigFromDetector( + job: Job, + detectorIndex: number, + metricFunctionDescription?: ES_AGGREGATION +) { const analysisConfig = job.analysis_config; const detector = analysisConfig.detectors[detectorIndex]; @@ -38,6 +48,9 @@ export function buildConfigFromDetector(job: Job, detectorIndex: number) { datafeedConfig: job.datafeed_config!, summaryCountFieldName: job.analysis_config.summary_count_field_name, }; + if (detector.function === ML_JOB_AGGREGATION.METRIC && metricFunctionDescription !== undefined) { + config.metricFunction = metricFunctionDescription; + } if (detector.field_name !== undefined) { config.metricFieldName = detector.field_name; diff --git a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts index 707a594d5eff3..345b6cf6054af 100644 --- a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts +++ b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts @@ -766,12 +766,12 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu } // Build the tooltip data for the chart info icon, showing further details on what is being plotted. - let functionLabel = `${config.metricFunction}`; + let functionLabel = `${fullSeriesConfig.metricFunction ?? config.metricFunction}`; if ( fullSeriesConfig.metricFieldName !== undefined && fullSeriesConfig.metricFieldName !== null ) { - functionLabel += ` ${fullSeriesConfig.metricFieldName}`; + functionLabel += `(${fullSeriesConfig.metricFieldName})`; } fullSeriesConfig.infoTooltip = { diff --git a/x-pack/plugins/ml/server/routes/schemas/results_service_schema.ts b/x-pack/plugins/ml/server/routes/schemas/results_service_schema.ts index 70fc5098fe3b5..656007b3e1e1c 100644 --- a/x-pack/plugins/ml/server/routes/schemas/results_service_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/results_service_schema.ts @@ -144,4 +144,5 @@ export const getAnomalyRecordsSchema = schema.object({ latestMs: schema.number(), criteriaFields: schema.arrayOf(schema.any()), interval: schema.string(), + functionDescription: schema.maybe(schema.nullable(schema.string())), });