Skip to content

Commit

Permalink
[ML] Support search for partitions on Single Metric Viewer (elastic#5…
Browse files Browse the repository at this point in the history
…3879)

* [ML] agg for partition field values

* [ML] change api

* [ML] load entity values

* [ML] check for partition field names

* [ML] wip

* [ML] refactor api

* [ML] debounce input

* [ML] remove Record, improve types, fix typo

* [ML] jobId as dedicated param, jsdoc comments

* [ML] result_type term based on model plot config

* [ML] remove redundant criteria for job id

* [ML] refactor getPartitionFieldsValues to TS

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
darnautov and elasticmachine committed Jan 7, 2020
1 parent 12e9fbd commit 4257a61
Show file tree
Hide file tree
Showing 11 changed files with 458 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import { DataFrameAnalyticsStats } from '../../data_frame_analytics/pages/analyt
import { JobMessage } from '../../../../common/types/audit_message';
import { DataFrameAnalyticsConfig } from '../../data_frame_analytics/common/analytics';
import { DeepPartial } from '../../../../common/types/common';
import { PartitionFieldsDefinition } from '../results_service/result_service_rx';
import { annotations } from './annotations';
import { Calendar, CalendarId, UpdateCalendar } from '../../../../common/types/calendars';
import { CombinedJob } from '../../jobs/new_job/common/job_creator/configs';
import { CombinedJob, JobId } from '../../jobs/new_job/common/job_creator/configs';

// TODO This is not a complete representation of all methods of `ml.*`.
// It just satisfies needs for other parts of the code area which use
Expand Down Expand Up @@ -124,6 +125,13 @@ declare interface Ml {

results: {
getMaxAnomalyScore: (jobIds: string[], earliestMs: number, latestMs: number) => Promise<any>;
fetchPartitionFieldsValues: (
jobId: JobId,
searchTerm: Record<string, string>,
criteriaFields: Array<{ fieldName: string; fieldValue: any }>,
earliestMs: number,
latestMs: number
) => Observable<PartitionFieldsDefinition>;
};

jobs: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,17 @@ export const results = {
},
});
},

fetchPartitionFieldsValues(jobId, searchTerm, criteriaFields, earliestMs, latestMs) {
return http$(`${basePath}/results/partition_fields_values`, {
method: 'POST',
body: {
jobId,
searchTerm,
criteriaFields,
earliestMs,
latestMs,
},
});
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getModelPlotOutput,
getRecordsForCriteria,
getScheduledEventsByBucket,
fetchPartitionFieldsValues,
} from './result_service_rx';
import {
getEventDistributionData,
Expand Down Expand Up @@ -42,6 +43,7 @@ export const mlResultsService = {
getEventDistributionData,
getModelPlotOutput,
getRecordMaxScoreByTime,
fetchPartitionFieldsValues,
};

type time = string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import _ from 'lodash';
import { Dictionary } from '../../../../common/types/common';
import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils';
import { JobId } from '../../jobs/new_job/common/job_creator/configs';
import { ml } from '../ml_api_service';
import { ML_RESULTS_INDEX_PATTERN } from '../../../../common/constants/index_patterns';
import { CriteriaField } from './index';
Expand All @@ -27,6 +29,23 @@ export interface MetricData extends ResultResponse {
results: Record<string, any>;
}

export interface FieldDefinition {
/**
* Partition field name.
*/
name: string | number;
/**
* Partitions field distinct values.
*/
values: any[];
}

type FieldTypes = 'partition_field' | 'over_field' | 'by_field';

export type PartitionFieldsDefinition = {
[field in FieldTypes]: FieldDefinition;
};

export function getMetricData(
index: string,
entityFields: any[],
Expand Down Expand Up @@ -532,3 +551,19 @@ export function getScheduledEventsByBucket(
})
);
}

export function fetchPartitionFieldsValues(
jobId: JobId,
searchTerm: Dictionary<string>,
criteriaFields: Array<{ fieldName: string; fieldValue: any }>,
earliestMs: number,
latestMs: number
) {
return ml.results.fetchPartitionFieldsValues(
jobId,
searchTerm,
criteriaFields,
earliestMs,
latestMs
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Component } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';

import {
EuiComboBox,
EuiComboBoxOptionProps,
EuiFlexItem,
EuiFormRow,
EuiToolTip,
} from '@elastic/eui';

export interface Entity {
fieldName: string;
fieldValue: any;
fieldValues: any;
}

function getEntityControlOptions(entity: Entity): EuiComboBoxOptionProps[] {
if (!Array.isArray(entity.fieldValues)) {
return [];
}

return entity.fieldValues.map(value => {
return { label: value };
});
}

interface EntityControlProps {
entity: Entity;
entityFieldValueChanged: (entity: Entity, fieldValue: any) => void;
onSearchChange: (entity: Entity, queryTerm: string) => void;
forceSelection: boolean;
}

interface EntityControlState {
selectedOptions: EuiComboBoxOptionProps[] | undefined;
isLoading: boolean;
options: EuiComboBoxOptionProps[] | undefined;
}

export class EntityControl extends Component<EntityControlProps, EntityControlState> {
inputRef: any;

state = {
selectedOptions: undefined,
options: undefined,
isLoading: false,
};

componentDidUpdate(prevProps: EntityControlProps) {
const { entity, forceSelection } = this.props;
const { selectedOptions } = this.state;

if (prevProps.entity === entity) {
return;
}

const { fieldValue } = entity;

const options = getEntityControlOptions(entity);

let selectedOptionsUpdate: EuiComboBoxOptionProps[] | undefined = selectedOptions;
if (
(selectedOptions === undefined && fieldValue.length > 0) ||
(Array.isArray(selectedOptions) &&
// @ts-ignore
selectedOptions[0].label !== fieldValue &&
fieldValue.length > 0)
) {
selectedOptionsUpdate = [{ label: fieldValue }];
} else if (Array.isArray(selectedOptions) && fieldValue.length === 0) {
selectedOptionsUpdate = undefined;
}

this.setState({
options,
isLoading: false,
selectedOptions: selectedOptionsUpdate,
});

if (forceSelection && this.inputRef) {
this.inputRef.focus();
}
}

onChange = (selectedOptions: EuiComboBoxOptionProps[]) => {
const options = selectedOptions.length > 0 ? selectedOptions : undefined;
this.setState({
selectedOptions: options,
});

const fieldValue =
Array.isArray(options) && options[0].label.length > 0 ? options[0].label : '';
this.props.entityFieldValueChanged(this.props.entity, fieldValue);
};

onSearchChange = (searchValue: string) => {
this.setState({
isLoading: true,
options: [],
});
this.props.onSearchChange(this.props.entity, searchValue);
};

render() {
const { entity, forceSelection } = this.props;
const { selectedOptions, isLoading, options } = this.state;

const control = (
<EuiComboBox
async
isLoading={isLoading}
inputRef={input => {
if (input) {
this.inputRef = input;
}
}}
style={{ minWidth: '300px' }}
placeholder={i18n.translate('xpack.ml.timeSeriesExplorer.enterValuePlaceholder', {
defaultMessage: 'Enter value',
})}
singleSelection={{ asPlainText: true }}
options={options}
selectedOptions={selectedOptions}
onChange={this.onChange}
onSearchChange={this.onSearchChange}
isClearable={false}
/>
);

const selectMessage = (
<FormattedMessage
id="xpack.ml.timeSeriesExplorer.selectFieldMessage"
defaultMessage="Select {fieldName}"
values={{ fieldName: entity.fieldName }}
/>
);

return (
<EuiFlexItem grow={false}>
<EuiFormRow label={entity.fieldName} helpText={forceSelection ? selectMessage : null}>
<EuiToolTip position="right" content={forceSelection ? selectMessage : null}>
{control}
</EuiToolTip>
</EuiFormRow>
</EuiFlexItem>
);
}
}
Loading

0 comments on commit 4257a61

Please sign in to comment.