Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Fixes Single Metric Viewer not showing chart for metric functions and mismatch function in tooltip #176354

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
peteharverson marked this conversation as resolved.
Show resolved Hide resolved
if (
fullSeriesConfig.metricFieldName !== undefined &&
fullSeriesConfig.metricFieldName !== null
) {
functionLabel += ` ${fullSeriesConfig.metricFieldName}`;
functionLabel += `(${fullSeriesConfig.metricFieldName})`;
}

fullSeriesConfig.infoTooltip = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())),
});