mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[ML] Prevents conditions on rules for rare, metric and lat_long (#21198)
This commit is contained in:
parent
967cb4e7fb
commit
755409d31d
6 changed files with 157 additions and 16 deletions
|
@ -31,3 +31,11 @@ export const OPERATOR = {
|
|||
GREATER_THAN: 'gt',
|
||||
GREATER_THAN_OR_EQUAL: 'gte',
|
||||
};
|
||||
|
||||
// List of detector functions which don't support rules with numeric conditions.
|
||||
export const CONDITIONS_NOT_SUPPORTED_FUNCTIONS = [
|
||||
'freq_rare',
|
||||
'lat_long',
|
||||
'metric',
|
||||
'rare',
|
||||
];
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
getEntityFieldValue,
|
||||
showActualForFunction,
|
||||
showTypicalForFunction,
|
||||
isRuleSupported,
|
||||
aggregationTypeTransform
|
||||
} from '../anomaly_utils';
|
||||
|
||||
|
@ -81,6 +82,103 @@ describe('ML - anomaly utils', () => {
|
|||
'field_name': 'responsetime'
|
||||
};
|
||||
|
||||
const metricNoEntityRecord = {
|
||||
'job_id': 'farequote_metric',
|
||||
'result_type': 'record',
|
||||
'probability': 0.030133495093182184,
|
||||
'record_score': 0.024881740359975164,
|
||||
'initial_record_score': 0.024881740359975164,
|
||||
'bucket_span': 900,
|
||||
'detector_index': 0,
|
||||
'is_interim': false,
|
||||
'timestamp': 1486845000000,
|
||||
'function': 'metric',
|
||||
'function_description': 'mean',
|
||||
'typical': [
|
||||
545.7764658569108
|
||||
],
|
||||
'actual': [
|
||||
758.8220213274412
|
||||
],
|
||||
'field_name': 'responsetime',
|
||||
'influencers': [
|
||||
{
|
||||
'influencer_field_name': 'airline',
|
||||
'influencer_field_values': [
|
||||
'NKS'
|
||||
]
|
||||
}
|
||||
],
|
||||
'airline': [
|
||||
'NKS'
|
||||
]
|
||||
};
|
||||
|
||||
const rareEntityRecord = {
|
||||
'job_id': 'gallery',
|
||||
'result_type': 'record',
|
||||
'probability': 0.02277014211908481,
|
||||
'record_score': 4.545378107075983,
|
||||
'initial_record_score': 4.545378107075983,
|
||||
'bucket_span': 3600,
|
||||
'detector_index': 0,
|
||||
'is_interim': false,
|
||||
'timestamp': 1495879200000,
|
||||
'by_field_name': 'status',
|
||||
'function': 'rare',
|
||||
'function_description': 'rare',
|
||||
'over_field_name': 'clientip',
|
||||
'over_field_value': '173.252.74.112',
|
||||
'causes': [
|
||||
{
|
||||
'probability': 0.02277014211908481,
|
||||
'by_field_name': 'status',
|
||||
'by_field_value': '206',
|
||||
'function': 'rare',
|
||||
'function_description': 'rare',
|
||||
'typical': [
|
||||
0.00014832458182211878
|
||||
],
|
||||
'actual': [
|
||||
1
|
||||
],
|
||||
'over_field_name': 'clientip',
|
||||
'over_field_value': '173.252.74.112'
|
||||
}
|
||||
],
|
||||
'influencers': [
|
||||
{
|
||||
'influencer_field_name': 'uri',
|
||||
'influencer_field_values': [
|
||||
'/wp-content/uploads/2013/06/dune_house_oil_on_canvas_24x20-298x298.jpg',
|
||||
'/wp-content/uploads/2013/10/Case-dAste-1-11-298x298.png'
|
||||
]
|
||||
},
|
||||
{
|
||||
'influencer_field_name': 'status',
|
||||
'influencer_field_values': [
|
||||
'206'
|
||||
]
|
||||
},
|
||||
{
|
||||
'influencer_field_name': 'clientip',
|
||||
'influencer_field_values': [
|
||||
'173.252.74.112'
|
||||
]
|
||||
}
|
||||
],
|
||||
'clientip': [
|
||||
'173.252.74.112'
|
||||
],
|
||||
'uri': [
|
||||
'/wp-content/uploads/2013/06/dune_house_oil_on_canvas_24x20-298x298.jpg',
|
||||
'/wp-content/uploads/2013/10/Case-dAste-1-11-298x298.png'
|
||||
],
|
||||
'status': [
|
||||
'206'
|
||||
]
|
||||
};
|
||||
|
||||
describe('getSeverity', () => {
|
||||
|
||||
it('returns warning for 0 <= score < 25', () => {
|
||||
|
@ -282,6 +380,21 @@ describe('ML - anomaly utils', () => {
|
|||
|
||||
});
|
||||
|
||||
describe('isRuleSupported', () => {
|
||||
it('returns true for anomalies supporting rules', () => {
|
||||
expect(isRuleSupported(partitionEntityRecord)).to.be(true);
|
||||
expect(isRuleSupported(byEntityRecord)).to.be(true);
|
||||
expect(isRuleSupported(overEntityRecord)).to.be(true);
|
||||
expect(isRuleSupported(rareEntityRecord)).to.be(true);
|
||||
expect(isRuleSupported(noEntityRecord)).to.be(true);
|
||||
});
|
||||
|
||||
it('returns false for anomaly not supporting rules', () => {
|
||||
expect(isRuleSupported(metricNoEntityRecord)).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('aggregationTypeTransform', () => {
|
||||
it('returns correct ES aggregation type for ML function description', () => {
|
||||
expect(aggregationTypeTransform.toES('count')).to.be('count');
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { CONDITIONS_NOT_SUPPORTED_FUNCTIONS } from '../constants/detector_rule';
|
||||
|
||||
// List of function descriptions for which actual values from record level results should be displayed.
|
||||
const DISPLAY_ACTUAL_FUNCTIONS = ['count', 'distinct_count', 'lat_long', 'mean', 'max', 'min', 'sum',
|
||||
|
@ -152,6 +153,14 @@ export function showTypicalForFunction(functionDescription) {
|
|||
return _.indexOf(DISPLAY_TYPICAL_FUNCTIONS, functionDescription) > -1;
|
||||
}
|
||||
|
||||
// Returns whether a rule can be configured against the specified anomaly.
|
||||
export function isRuleSupported(record) {
|
||||
// A rule can be configured with a numeric condition if the function supports it,
|
||||
// and/or with scope if there is a partitioning fields.
|
||||
return (CONDITIONS_NOT_SUPPORTED_FUNCTIONS.indexOf(record.function) === -1) ||
|
||||
(getEntityFieldName(record) !== undefined);
|
||||
}
|
||||
|
||||
// Two functions for converting aggregation type names.
|
||||
// ML and ES use different names for the same function.
|
||||
// Possible values for ML aggregation type are (defined in lib/model/CAnomalyDetector.cc):
|
||||
|
|
|
@ -37,7 +37,7 @@ import { checkPermission } from 'plugins/ml/privilege/check_privilege';
|
|||
|
||||
import { mlAnomaliesTableService } from './anomalies_table_service';
|
||||
import { mlFieldFormatService } from 'plugins/ml/services/field_format_service';
|
||||
import { getSeverityColor } from 'plugins/ml/../common/util/anomaly_utils';
|
||||
import { getSeverityColor, isRuleSupported } from 'plugins/ml/../common/util/anomaly_utils';
|
||||
import { formatValue } from 'plugins/ml/formatters/format_value';
|
||||
import { RuleEditorFlyout } from 'plugins/ml/components/rule_editor';
|
||||
|
||||
|
@ -56,8 +56,8 @@ function renderTime(date, aggregationInterval) {
|
|||
}
|
||||
|
||||
function showLinksMenuForItem(item) {
|
||||
const canUpdateJob = checkPermission('canUpdateJob');
|
||||
return (canUpdateJob ||
|
||||
const canConfigureRules = (isRuleSupported(item) && checkPermission('canUpdateJob'));
|
||||
return (canConfigureRules ||
|
||||
item.isTimeSeriesViewDetector ||
|
||||
item.entityName === 'mlcategory' ||
|
||||
item.customUrls !== undefined);
|
||||
|
|
|
@ -23,6 +23,7 @@ import { toastNotifications } from 'ui/notify';
|
|||
|
||||
import { ES_FIELD_TYPES } from 'plugins/ml/../common/constants/field_types';
|
||||
import { checkPermission } from 'plugins/ml/privilege/check_privilege';
|
||||
import { isRuleSupported } from 'plugins/ml/../common/util/anomaly_utils';
|
||||
import { parseInterval } from 'plugins/ml/../common/util/parse_interval';
|
||||
import { getFieldTypeFromMapping } from 'plugins/ml/services/mapping_service';
|
||||
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||
|
@ -336,7 +337,7 @@ export class LinksMenu extends Component {
|
|||
|
||||
render() {
|
||||
const { anomaly, showViewSeriesLink } = this.props;
|
||||
const canUpdateJob = checkPermission('canUpdateJob');
|
||||
const canConfigureRules = (isRuleSupported(anomaly.source) && checkPermission('canUpdateJob'));
|
||||
|
||||
const button = (
|
||||
<EuiButtonIcon
|
||||
|
@ -387,7 +388,7 @@ export class LinksMenu extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (canUpdateJob) {
|
||||
if (canConfigureRules) {
|
||||
items.push(
|
||||
<EuiContextMenuItem
|
||||
key="create_rule"
|
||||
|
|
|
@ -44,7 +44,7 @@ import {
|
|||
deleteJobRule
|
||||
} from './utils';
|
||||
|
||||
import { ACTION } from '../../../common/constants/detector_rule';
|
||||
import { ACTION, CONDITIONS_NOT_SUPPORTED_FUNCTIONS } from '../../../common/constants/detector_rule';
|
||||
import { getPartitioningFieldNames } from 'plugins/ml/../common/util/job_utils';
|
||||
import { mlJobService } from 'plugins/ml/services/job_service';
|
||||
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||
|
@ -366,8 +366,6 @@ export class RuleEditorFlyout extends Component {
|
|||
|
||||
let flyout;
|
||||
|
||||
const hasPartitioningFields = (this.partitioningFieldNames && this.partitioningFieldNames.length > 0);
|
||||
|
||||
if (ruleIndex === -1) {
|
||||
flyout = (
|
||||
<EuiFlyout
|
||||
|
@ -409,9 +407,12 @@ export class RuleEditorFlyout extends Component {
|
|||
</EuiFlyout>
|
||||
);
|
||||
} else {
|
||||
const hasPartitioningFields = (this.partitioningFieldNames && this.partitioningFieldNames.length > 0);
|
||||
const conditionSupported = (CONDITIONS_NOT_SUPPORTED_FUNCTIONS.indexOf(anomaly.source.function) === -1);
|
||||
const conditionsText = 'Add numeric conditions to take action according ' +
|
||||
'to the actual or typical values of the anomaly. Multiple conditions are ' +
|
||||
'combined using AND.';
|
||||
|
||||
flyout = (
|
||||
<EuiFlyout
|
||||
className="ml-rule-editor-flyout"
|
||||
|
@ -452,14 +453,23 @@ export class RuleEditorFlyout extends Component {
|
|||
<h2>Conditions</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiCheckbox
|
||||
id="enable_conditions_checkbox"
|
||||
className="scope-enable-checkbox"
|
||||
label={conditionsText}
|
||||
checked={isConditionsEnabled}
|
||||
onChange={this.onConditionsEnabledChange}
|
||||
disabled={!hasPartitioningFields}
|
||||
/>
|
||||
{(conditionSupported === true) ?
|
||||
(
|
||||
<EuiCheckbox
|
||||
id="enable_conditions_checkbox"
|
||||
className="scope-enable-checkbox"
|
||||
label={conditionsText}
|
||||
checked={isConditionsEnabled}
|
||||
onChange={this.onConditionsEnabledChange}
|
||||
disabled={!conditionSupported || !hasPartitioningFields}
|
||||
/>
|
||||
) : (
|
||||
<EuiCallOut
|
||||
title={`Conditions are not supported for detectors using the ${anomaly.source.function} function`}
|
||||
iconType="iInCircle"
|
||||
/>
|
||||
)
|
||||
}
|
||||
<EuiSpacer size="s" />
|
||||
<ConditionsSection
|
||||
isEnabled={isConditionsEnabled}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue