Skip to content

Commit

Permalink
[Maps][ML] Integration follow up: adds partition field info to map po…
Browse files Browse the repository at this point in the history
…int tooltip if available (elastic#123516)

* add partition field info to tooltip if available

* make partition field in tooltip consistent. simplify result fetch

* make layers const

* remove unnecessary type

* fix types
  • Loading branch information
alvarezmelissa87 authored Jan 25, 2022
1 parent 403fdcb commit 2ebc8d2
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 80 deletions.
11 changes: 7 additions & 4 deletions x-pack/plugins/ml/public/maps/anomaly_source.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import type { SourceEditorArgs } from '../../../maps/public';
import type { DataRequest } from '../../../maps/public';
import type { IVectorSource, SourceStatus } from '../../../maps/public';
import { ML_ANOMALY } from './anomaly_source_factory';
import { getResultsForJobId, MlAnomalyLayers } from './util';
import { getResultsForJobId, ML_ANOMALY_LAYERS, MlAnomalyLayersType } from './util';
import { UpdateAnomalySourceEditor } from './update_anomaly_source_editor';
import type { MlApiServices } from '../application/services/ml_api_service';

export interface AnomalySourceDescriptor extends AbstractSourceDescriptor {
jobId: string;
typicalActual: MlAnomalyLayers;
typicalActual: MlAnomalyLayersType;
}

export class AnomalySource implements IVectorSource {
Expand All @@ -50,7 +50,7 @@ export class AnomalySource implements IVectorSource {
return {
type: ML_ANOMALY,
jobId: descriptor.jobId,
typicalActual: descriptor.typicalActual || 'actual',
typicalActual: descriptor.typicalActual || ML_ANOMALY_LAYERS.ACTUAL,
};
}

Expand Down Expand Up @@ -232,7 +232,7 @@ export class AnomalySource implements IVectorSource {
}

async getSupportedShapeTypes(): Promise<VECTOR_SHAPE_TYPE[]> {
return this._descriptor.typicalActual === 'connected'
return this._descriptor.typicalActual === ML_ANOMALY_LAYERS.TYPICAL_TO_ACTUAL
? [VECTOR_SHAPE_TYPE.LINE]
: [VECTOR_SHAPE_TYPE.POINT];
}
Expand All @@ -251,6 +251,9 @@ export class AnomalySource implements IVectorSource {
const label = ANOMALY_SOURCE_FIELDS[key]?.label;
if (label) {
tooltipProperties.push(new AnomalySourceTooltipProperty(label, properties[key]));
} else if (!ANOMALY_SOURCE_FIELDS[key]) {
// partition field keys will be different each time so won't be in ANOMALY_SOURCE_FIELDS
tooltipProperties.push(new AnomalySourceTooltipProperty(key, properties[key]));
}
}
}
Expand Down
56 changes: 51 additions & 5 deletions x-pack/plugins/ml/public/maps/anomaly_source_field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ import { AnomalySource } from './anomaly_source';
import { ITooltipProperty } from '../../../maps/public';
import { Filter } from '../../../../../src/plugins/data/public';

export const ACTUAL_LABEL = i18n.translate('xpack.ml.maps.anomalyLayerActualLabel', {
defaultMessage: 'Actual',
});
export const TYPICAL_LABEL = i18n.translate('xpack.ml.maps.anomalyLayerTypicalLabel', {
defaultMessage: 'Typical',
});
export const TYPICAL_TO_ACTUAL = i18n.translate('xpack.ml.maps.anomalyLayerTypicalToActualLabel', {
defaultMessage: 'Typical to actual',
});

export const ANOMALY_SOURCE_FIELDS: Record<string, Record<string, string>> = {
record_score: {
label: i18n.translate('xpack.ml.maps.anomalyLayerRecordScoreLabel', {
Expand All @@ -40,15 +50,51 @@ export const ANOMALY_SOURCE_FIELDS: Record<string, Record<string, string>> = {
}),
type: 'string',
},
// this value is only used to place the point on the map
actual: {},
actualDisplay: {
label: i18n.translate('xpack.ml.maps.anomalyLayerActualLabel', {
defaultMessage: 'Actual',
}),
label: ACTUAL_LABEL,
type: 'string',
},
// this value is only used to place the point on the map
typical: {},
typicalDisplay: {
label: i18n.translate('xpack.ml.maps.anomalyLayerTypicalLabel', {
defaultMessage: 'Typical',
label: TYPICAL_LABEL,
type: 'string',
},
partition_field_name: {
label: i18n.translate('xpack.ml.maps.anomalyLayerPartitionFieldNameLabel', {
defaultMessage: 'Partition field name',
}),
type: 'string',
},
partition_field_value: {
label: i18n.translate('xpack.ml.maps.anomalyLayerPartitionFieldValueLabel', {
defaultMessage: 'Partition field value',
}),
type: 'string',
},
by_field_name: {
label: i18n.translate('xpack.ml.maps.anomalyLayerByFieldNameLabel', {
defaultMessage: 'By field name',
}),
type: 'string',
},
by_field_value: {
label: i18n.translate('xpack.ml.maps.anomalyLayerByFieldValueLabel', {
defaultMessage: 'By field value',
}),
type: 'string',
},
over_field_name: {
label: i18n.translate('xpack.ml.maps.anomalyLayerOverFieldNameLabel', {
defaultMessage: 'Over field name',
}),
type: 'string',
},
over_field_value: {
label: i18n.translate('xpack.ml.maps.anomalyLayerOverFieldValueLabel', {
defaultMessage: 'Over field value',
}),
type: 'string',
},
Expand Down
10 changes: 5 additions & 5 deletions x-pack/plugins/ml/public/maps/create_anomaly_source_editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { EuiPanel } from '@elastic/eui';
import { AnomalySourceDescriptor } from './anomaly_source';
import { AnomalyJobSelector } from './anomaly_job_selector';
import { LayerSelector } from './layer_selector';
import { MlAnomalyLayers } from './util';
import { ML_ANOMALY_LAYERS, MlAnomalyLayersType } from './util';
import type { MlApiServices } from '../application/services/ml_api_service';

interface Props {
Expand All @@ -21,7 +21,7 @@ interface Props {

interface State {
jobId?: string;
typicalActual?: MlAnomalyLayers;
typicalActual?: MlAnomalyLayersType;
}

export class CreateAnomalySourceEditor extends Component<Props, State> {
Expand All @@ -32,7 +32,7 @@ export class CreateAnomalySourceEditor extends Component<Props, State> {
if (this.state.jobId) {
this.props.onSourceConfigChange({
jobId: this.state.jobId,
typicalActual: this.state.typicalActual,
typicalActual: this.state.typicalActual || ML_ANOMALY_LAYERS.ACTUAL,
});
}
}
Expand All @@ -41,7 +41,7 @@ export class CreateAnomalySourceEditor extends Component<Props, State> {
this._isMounted = true;
}

private onTypicalActualChange = (typicalActual: MlAnomalyLayers) => {
private onTypicalActualChange = (typicalActual: MlAnomalyLayersType) => {
if (!this._isMounted) {
return;
}
Expand Down Expand Up @@ -73,7 +73,7 @@ export class CreateAnomalySourceEditor extends Component<Props, State> {
const selector = this.state.jobId ? (
<LayerSelector
onChange={this.onTypicalActualChange}
typicalActual={this.state.typicalActual || 'actual'}
typicalActual={this.state.typicalActual || ML_ANOMALY_LAYERS.ACTUAL}
/>
) : null;
return (
Expand Down
27 changes: 17 additions & 10 deletions x-pack/plugins/ml/public/maps/layer_selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import React, { Component } from 'react';

import { EuiComboBox, EuiFormRow, EuiComboBoxOptionOption } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { MlAnomalyLayers } from './util';
import { ML_ANOMALY_LAYERS, MlAnomalyLayersType } from './util';
import { ACTUAL_LABEL, TYPICAL_LABEL, TYPICAL_TO_ACTUAL } from './anomaly_source_field';

interface Props {
onChange: (typicalActual: MlAnomalyLayers) => void;
typicalActual: MlAnomalyLayers;
onChange: (typicalActual: MlAnomalyLayersType) => void;
typicalActual: MlAnomalyLayersType;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand All @@ -33,10 +34,7 @@ export class LayerSelector extends Component<Props, State> {
}

onSelect = (selectedOptions: Array<EuiComboBoxOptionOption<string>>) => {
const typicalActual: MlAnomalyLayers = selectedOptions[0].value! as
| 'typical'
| 'actual'
| 'connected';
const typicalActual: MlAnomalyLayersType = selectedOptions[0].value! as MlAnomalyLayersType;
if (this._isMounted) {
this.setState({ typicalActual });
this.props.onChange(typicalActual);
Expand All @@ -56,9 +54,18 @@ export class LayerSelector extends Component<Props, State> {
singleSelection={true}
onChange={this.onSelect}
options={[
{ value: 'actual', label: 'actual' },
{ value: 'typical', label: 'typical' },
{ value: 'connected', label: 'connected' },
{
value: ML_ANOMALY_LAYERS.ACTUAL,
label: ACTUAL_LABEL,
},
{
value: ML_ANOMALY_LAYERS.TYPICAL,
label: TYPICAL_LABEL,
},
{
value: ML_ANOMALY_LAYERS.TYPICAL_TO_ACTUAL,
label: TYPICAL_TO_ACTUAL,
},
]}
selectedOptions={options}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import React, { Fragment, Component } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
import { LayerSelector } from './layer_selector';
import { MlAnomalyLayers } from './util';
import { MlAnomalyLayersType } from './util';

interface Props {
onChange: (...args: Array<{ propName: string; value: unknown }>) => void;
typicalActual: MlAnomalyLayers;
typicalActual: MlAnomalyLayersType;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand All @@ -34,7 +34,7 @@ export class UpdateAnomalySourceEditor extends Component<Props, State> {
</EuiTitle>
<EuiSpacer size="s" />
<LayerSelector
onChange={(typicalActual: MlAnomalyLayers) => {
onChange={(typicalActual: MlAnomalyLayersType) => {
this.props.onChange({
propName: 'typicalActual',
value: typicalActual,
Expand Down
97 changes: 44 additions & 53 deletions x-pack/plugins/ml/public/maps/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import type { MlApiServices } from '../application/services/ml_api_service';
import { MLAnomalyDoc } from '../../common/types/anomalies';
import { VectorSourceRequestMeta } from '../../../maps/common';

export type MlAnomalyLayers = 'typical' | 'actual' | 'connected';
export const ML_ANOMALY_LAYERS = {
TYPICAL: 'typical',
ACTUAL: 'actual',
TYPICAL_TO_ACTUAL: 'typical to actual',
} as const;

export type MlAnomalyLayersType = typeof ML_ANOMALY_LAYERS[keyof typeof ML_ANOMALY_LAYERS];

// Must reverse coordinates here. Map expects [lon, lat] - anomalies are stored as [lat, lon] for lat_lon jobs
function getCoordinates(actualCoordinateStr: string, round: boolean = false): number[] {
Expand All @@ -28,7 +34,7 @@ function getCoordinates(actualCoordinateStr: string, round: boolean = false): nu
export async function getResultsForJobId(
mlResultsService: MlApiServices['results'],
jobId: string,
locationType: MlAnomalyLayers,
locationType: MlAnomalyLayersType,
searchFilters: VectorSourceRequestMeta
): Promise<FeatureCollection> {
const { timeFilters } = searchFilters;
Expand Down Expand Up @@ -64,16 +70,6 @@ export async function getResultsForJobId(
}

let resp: ESSearchResponse<MLAnomalyDoc> | null = null;
let hits: Array<{
actual: number[];
actualDisplay: number[];
fieldName?: string;
functionDescription: string;
typical: number[];
typicalDisplay: number[];
record_score: number;
timestamp: string;
}> = [];

try {
resp = await mlResultsService.anomalySearch(
Expand All @@ -86,8 +82,9 @@ export async function getResultsForJobId(
// search may fail if the job doesn't already exist
// ignore this error as the outer function call will raise a toast
}
if (resp !== null && resp.hits.total.value > 0) {
hits = resp.hits.hits.map(({ _source }) => {

const features: Feature[] =
resp?.hits.hits.map(({ _source }) => {
const geoResults = _source.geo_results;
const actualCoordStr = geoResults && geoResults.actual_point;
const typicalCoordStr = geoResults && geoResults.typical_point;
Expand All @@ -104,47 +101,41 @@ export async function getResultsForJobId(
typical = getCoordinates(typicalCoordStr);
typicalDisplay = getCoordinates(typicalCoordStr, true);
}
return {
fieldName: _source.field_name,
functionDescription: _source.function_description,
timestamp: formatHumanReadableDateTimeSeconds(_source.timestamp),
typical,
typicalDisplay,
actual,
actualDisplay,
record_score: Math.floor(_source.record_score),
};
});
}

const features: Feature[] = hits.map((result) => {
let geometry: Geometry;
if (locationType === 'typical' || locationType === 'actual') {
geometry = {
type: 'Point',
coordinates: locationType === 'typical' ? result.typical : result.actual,
};
} else {
geometry = {
type: 'LineString',
coordinates: [result.typical, result.actual],
let geometry: Geometry;
if (locationType === ML_ANOMALY_LAYERS.TYPICAL || locationType === ML_ANOMALY_LAYERS.ACTUAL) {
geometry = {
type: 'Point',
coordinates: locationType === ML_ANOMALY_LAYERS.TYPICAL ? typical : actual,
};
} else {
geometry = {
type: 'LineString',
coordinates: [typical, actual],
};
}
return {
type: 'Feature',
geometry,
properties: {
actual,
actualDisplay,
typical,
typicalDisplay,
fieldName: _source.field_name,
functionDescription: _source.function_description,
timestamp: formatHumanReadableDateTimeSeconds(_source.timestamp),
record_score: Math.floor(_source.record_score),
...(_source.partition_field_name
? { [_source.partition_field_name]: _source.partition_field_value }
: {}),
...(_source.by_field_name ? { [_source.by_field_name]: _source.by_field_value } : {}),
...(_source.over_field_name
? { [_source.over_field_name]: _source.over_field_value }
: {}),
},
};
}
return {
type: 'Feature',
geometry,
properties: {
actual: result.actual,
actualDisplay: result.actualDisplay,
typical: result.typical,
typicalDisplay: result.typicalDisplay,
fieldName: result.fieldName,
functionDescription: result.functionDescription,
timestamp: result.timestamp,
record_score: result.record_score,
},
};
});
}) || [];

return {
type: 'FeatureCollection',
Expand Down

0 comments on commit 2ebc8d2

Please sign in to comment.