diff --git a/x-pack/plugins/ml/common/util/job_utils.d.ts b/x-pack/plugins/ml/common/util/job_utils.d.ts index 4528fbfbb774d..170e42aabc67d 100644 --- a/x-pack/plugins/ml/common/util/job_utils.d.ts +++ b/x-pack/plugins/ml/common/util/job_utils.d.ts @@ -44,6 +44,8 @@ export function mlFunctionToESAggregation(functionName: string): string | null; export function isModelPlotEnabled(job: Job, detectorIndex: number, entityFields: any[]): boolean; +export function isModelPlotChartableForDetector(job: Job, detectorIndex: number): boolean; + export function getSafeAggregationName(fieldName: string, index: number): string; export function getLatestDataOrBucketTimestamp( diff --git a/x-pack/plugins/ml/common/util/job_utils.js b/x-pack/plugins/ml/common/util/job_utils.js index 8fe5733ce67bd..1217139872fc1 100644 --- a/x-pack/plugins/ml/common/util/job_utils.js +++ b/x-pack/plugins/ml/common/util/job_utils.js @@ -105,18 +105,20 @@ export function isModelPlotChartableForDetector(job, detectorIndex) { const dtr = dtrs[detectorIndex]; const functionName = dtr.function; - // Model plot can be charted for any of the functions which map to ES aggregations, + // Model plot can be charted for any of the functions which map to ES aggregations + // (except rare, for which no model plot results are generated), // plus varp and info_content functions. isModelPlotChartable = - mlFunctionToESAggregation(functionName) !== null || - [ - 'varp', - 'high_varp', - 'low_varp', - 'info_content', - 'high_info_content', - 'low_info_content', - ].includes(functionName) === true; + functionName !== 'rare' && + (mlFunctionToESAggregation(functionName) !== null || + [ + 'varp', + 'high_varp', + 'low_varp', + 'info_content', + 'high_info_content', + 'low_info_content', + ].includes(functionName) === true); } return isModelPlotChartable; diff --git a/x-pack/plugins/ml/common/util/job_utils.test.js b/x-pack/plugins/ml/common/util/job_utils.test.js index a5df160bdf5ca..de269676a96ed 100644 --- a/x-pack/plugins/ml/common/util/job_utils.test.js +++ b/x-pack/plugins/ml/common/util/job_utils.test.js @@ -307,7 +307,14 @@ describe('ML - job utils', () => { const job2 = { analysis_config: { - detectors: [{ function: 'count' }, { function: 'info_content' }], + detectors: [ + { function: 'count' }, + { function: 'info_content' }, + { + function: 'rare', + by_field_name: 'mlcategory', + }, + ], }, model_plot_config: { enabled: true, @@ -325,6 +332,10 @@ describe('ML - job utils', () => { test('returns true for info_content detector when model plot is enabled', () => { expect(isModelPlotChartableForDetector(job2, 1)).toBe(true); }); + + test('returns false for rare by mlcategory when model plot is enabled', () => { + expect(isModelPlotChartableForDetector(job2, 2)).toBe(false); + }); }); describe('getPartitioningFieldNames', () => { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js index e0fb97a81f587..fe7e436b61117 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js @@ -19,6 +19,7 @@ import { chartLimits, getChartType } from '../../util/chart_utils'; import { getEntityFieldList } from '../../../../common/util/anomaly_utils'; import { isSourceDataChartableForDetector, + isModelPlotChartableForDetector, isModelPlotEnabled, } from '../../../../common/util/job_utils'; import { mlResultsService } from '../../services/results_service'; @@ -420,7 +421,7 @@ function processRecordsForDisplay(anomalyRecords) { // is chartable, and if model plot is enabled for the job. const job = mlJobService.getJob(record.job_id); let isChartable = isSourceDataChartableForDetector(job, record.detector_index); - if (isChartable === false) { + if (isChartable === false && isModelPlotChartableForDetector(job, record.detector_index)) { // Check if model plot is enabled for this job. // Need to check the entity fields for the record in case the model plot config has a terms list. const entityFields = getEntityFieldList(record); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js index 3fcb3c351e666..aaf9ff491ce32 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js @@ -20,6 +20,7 @@ import { import { getEntityFieldList } from '../../../common/util/anomaly_utils'; import { isSourceDataChartableForDetector, + isModelPlotChartableForDetector, isModelPlotEnabled, } from '../../../common/util/job_utils'; import { parseInterval } from '../../../common/util/parse_interval'; @@ -636,7 +637,10 @@ export async function loadAnomaliesTableData( // TODO - when job_service is moved server_side, move this to server endpoint. const job = mlJobService.getJob(jobId); let isChartable = isSourceDataChartableForDetector(job, anomaly.detectorIndex); - if (isChartable === false) { + if ( + isChartable === false && + isModelPlotChartableForDetector(job, anomaly.detectorIndex) + ) { // Check if model plot is enabled for this job. // Need to check the entity fields for the record in case the model plot config has a terms list. // If terms is specified, model plot is only stored if both the partition and by fields appear in the list. 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 f973d41ad7754..6e46ab0023ce4 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 @@ -9,7 +9,10 @@ import _ from 'lodash'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ml } from '../services/ml_api_service'; -import { isModelPlotEnabled } from '../../../common/util/job_utils'; +import { + isModelPlotChartableForDetector, + isModelPlotEnabled, +} from '../../../common/util/job_utils'; // @ts-ignore import { buildConfigFromDetector } from '../util/chart_config_builder'; import { mlResultsService } from '../services/results_service'; @@ -24,7 +27,10 @@ function getMetricData( latestMs: number, interval: string ): Observable { - if (isModelPlotEnabled(job, detectorIndex, entityFields)) { + if ( + isModelPlotChartableForDetector(job, detectorIndex) && + isModelPlotEnabled(job, detectorIndex, entityFields) + ) { // Extract the partition, by, over fields on which to filter. const criteriaFields = []; const detector = job.analysis_config.detectors[detectorIndex]; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 45f495299bc69..8bf42fe545152 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -36,6 +36,7 @@ import { ResizeChecker } from '../../../../../../src/plugins/kibana_utils/public import { ANOMALIES_TABLE_DEFAULT_QUERY_SIZE } from '../../../common/constants/search'; import { isModelPlotEnabled, + isModelPlotChartableForDetector, isSourceDataChartableForDetector, isTimeSeriesViewDetector, mlFunctionToESAggregation, @@ -506,11 +507,9 @@ export class TimeSeriesExplorer extends React.Component { contextForecastData: undefined, focusChartData: undefined, focusForecastData: undefined, - modelPlotEnabled: isModelPlotEnabled( - currentSelectedJob, - selectedDetectorIndex, - entityControls - ), + modelPlotEnabled: + isModelPlotChartableForDetector(currentSelectedJob, selectedDetectorIndex) && + isModelPlotEnabled(currentSelectedJob, selectedDetectorIndex, entityControls), hasResults: false, dataNotChartable: false, }