Skip to content

Commit be50f81

Browse files
committed
[ML] Fix query bar autocompletion for ML and AIOps embeddables (#164485)
(cherry picked from commit 5d5ac37)
1 parent ea9ec1f commit be50f81

6 files changed

Lines changed: 116 additions & 73 deletions

File tree

x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,31 @@ export class EmbeddableChangePointChart extends AbstractEmbeddable<
5858

5959
private node?: HTMLElement;
6060

61+
// Need to defer embeddable load in order to resolve data views
62+
deferEmbeddableLoad = true;
63+
6164
constructor(
6265
private readonly deps: EmbeddableChangePointChartDeps,
6366
initialInput: EmbeddableChangePointChartInput,
6467
parent?: IContainer
6568
) {
6669
super(initialInput, { defaultTitle: initialInput.title }, parent);
70+
71+
this.initOutput().finally(() => this.setInitializationFinished());
72+
}
73+
74+
private async initOutput() {
75+
const {
76+
data: { dataViews: dataViewsService },
77+
} = this.deps;
78+
79+
const { dataViewId } = this.getInput();
80+
81+
const dataView = await dataViewsService.get(dataViewId);
82+
83+
this.updateOutput({
84+
indexPatterns: [dataView],
85+
});
6786
}
6887

6988
public reportsEmbeddableLoad() {

x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
import { type Observable } from 'rxjs';
9-
import React, { FC, useCallback, useEffect, useMemo } from 'react';
9+
import React, { FC, useEffect, useMemo } from 'react';
1010
import { useTimefilter } from '@kbn/ml-date-picker';
1111
import { css } from '@emotion/react';
1212
import useObservable from 'react-use/lib/useObservable';
@@ -21,11 +21,7 @@ import type {
2121
} from './embeddable_change_point_chart';
2222
import { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component';
2323
import { FilterQueryContextProvider, useFilerQueryUpdates } from '../hooks/use_filters_query';
24-
import {
25-
DataSourceContextProvider,
26-
type DataSourceContextProviderProps,
27-
useDataSource,
28-
} from '../hooks/use_data_source';
24+
import { DataSourceContextProvider, useDataSource } from '../hooks/use_data_source';
2925
import { useAiopsAppContext } from '../hooks/use_aiops_app_context';
3026
import { useTimeBuckets } from '../hooks/use_time_buckets';
3127
import { createMergedEsQuery } from '../application/utils/search_utils';
@@ -59,16 +55,9 @@ export const EmbeddableInputTracker: FC<EmbeddableInputTrackerProps> = ({
5955
}) => {
6056
const input = useObservable(input$, initialInput);
6157

62-
const onChange = useCallback<Exclude<DataSourceContextProviderProps['onChange'], undefined>>(
63-
({ dataViews }) => {
64-
onOutputChange({ indexPatterns: dataViews });
65-
},
66-
[onOutputChange]
67-
);
68-
6958
return (
7059
<ReloadContextProvider reload$={reload$}>
71-
<DataSourceContextProvider dataViewId={input.dataViewId} onChange={onChange}>
60+
<DataSourceContextProvider dataViewId={input.dataViewId}>
7261
<FilterQueryContextProvider timeRange={input.timeRange}>
7362
<ChartGridEmbeddableWrapper
7463
timeRange={input.timeRange}

x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import { CoreStart } from '@kbn/core/public';
1111
import { i18n } from '@kbn/i18n';
1212
import { Subject } from 'rxjs';
1313
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
14-
import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public';
15-
import type { DataView } from '@kbn/data-views-plugin/common';
14+
import { IContainer } from '@kbn/embeddable-plugin/public';
1615
import { EmbeddableAnomalyChartsContainer } from './embeddable_anomaly_charts_container_lazy';
1716
import type { JobId } from '../../../common/types/anomaly_detection_jobs';
1817
import type { MlDependencies } from '../../application/app';
@@ -23,6 +22,8 @@ import {
2322
AnomalyChartsServices,
2423
} from '..';
2524
import { EmbeddableLoading } from '../common/components/embeddable_loading_fallback';
25+
import { AnomalyDetectionEmbeddable } from '../common/anomaly_detection_embeddable';
26+
2627
export const getDefaultExplorerChartsPanelTitle = (jobIds: JobId[]) =>
2728
i18n.translate('xpack.ml.anomalyChartsEmbeddable.title', {
2829
defaultMessage: 'ML anomaly charts for {jobIds}',
@@ -31,7 +32,7 @@ export const getDefaultExplorerChartsPanelTitle = (jobIds: JobId[]) =>
3132

3233
export type IAnomalyChartsEmbeddable = typeof AnomalyChartsEmbeddable;
3334

34-
export class AnomalyChartsEmbeddable extends Embeddable<
35+
export class AnomalyChartsEmbeddable extends AnomalyDetectionEmbeddable<
3536
AnomalyChartsEmbeddableInput,
3637
AnomalyChartsEmbeddableOutput
3738
> {
@@ -44,52 +45,7 @@ export class AnomalyChartsEmbeddable extends Embeddable<
4445
public services: [CoreStart, MlDependencies, AnomalyChartsServices],
4546
parent?: IContainer
4647
) {
47-
super(
48-
initialInput,
49-
{
50-
defaultTitle: initialInput.title,
51-
defaultDescription: initialInput.description,
52-
},
53-
parent
54-
);
55-
this.initializeOutput(initialInput);
56-
}
57-
58-
private async initializeOutput(initialInput: AnomalyChartsEmbeddableInput) {
59-
const { anomalyExplorerService } = this.services[2];
60-
const { jobIds } = initialInput;
61-
62-
try {
63-
const jobs = await anomalyExplorerService.getCombinedJobs(jobIds);
64-
const dataViewsService = this.services[1].data.dataViews;
65-
66-
// First get list of unique indices from the selected jobs
67-
const indices = new Set(jobs.map((j) => j.datafeed_config.indices).flat());
68-
// Then find the data view assuming the data view title matches the index name
69-
const indexPatterns: Record<string, DataView> = {};
70-
for (const indexName of indices) {
71-
const response = await dataViewsService.find(`"${indexName}"`);
72-
73-
const indexPattern = response.find(
74-
(obj) => obj.title.toLowerCase() === indexName.toLowerCase()
75-
);
76-
if (indexPattern !== undefined) {
77-
indexPatterns[indexPattern.id!] = indexPattern;
78-
}
79-
}
80-
81-
this.updateOutput({
82-
...this.getOutput(),
83-
indexPatterns: Object.values(indexPatterns),
84-
});
85-
} catch (e) {
86-
// Unable to find and load data view but we can ignore the error
87-
// as we only load it to support the filter & query bar
88-
// the visualizations should still work correctly
89-
90-
// eslint-disable-next-line no-console
91-
console.error(`Unable to load data views for ${jobIds}`, e);
92-
}
48+
super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent);
9349
}
9450

9551
public onLoading() {

x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { CoreStart } from '@kbn/core/public';
1111
import { i18n } from '@kbn/i18n';
1212
import { Subject } from 'rxjs';
1313
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
14-
import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public';
14+
import { IContainer } from '@kbn/embeddable-plugin/public';
1515
import { EmbeddableSwimLaneContainer } from './embeddable_swim_lane_container_lazy';
1616
import type { JobId } from '../../../common/types/anomaly_detection_jobs';
1717
import type { MlDependencies } from '../../application/app';
@@ -23,6 +23,7 @@ import {
2323
AnomalySwimlaneServices,
2424
} from '..';
2525
import { EmbeddableLoading } from '../common/components/embeddable_loading_fallback';
26+
import { AnomalyDetectionEmbeddable } from '../common/anomaly_detection_embeddable';
2627

2728
export const getDefaultSwimlanePanelTitle = (jobIds: JobId[]) =>
2829
i18n.translate('xpack.ml.swimlaneEmbeddable.title', {
@@ -32,7 +33,7 @@ export const getDefaultSwimlanePanelTitle = (jobIds: JobId[]) =>
3233

3334
export type IAnomalySwimlaneEmbeddable = typeof AnomalySwimlaneEmbeddable;
3435

35-
export class AnomalySwimlaneEmbeddable extends Embeddable<
36+
export class AnomalySwimlaneEmbeddable extends AnomalyDetectionEmbeddable<
3637
AnomalySwimlaneEmbeddableInput,
3738
AnomalySwimlaneEmbeddableOutput
3839
> {
@@ -45,14 +46,7 @@ export class AnomalySwimlaneEmbeddable extends Embeddable<
4546
public services: [CoreStart, MlDependencies, AnomalySwimlaneServices],
4647
parent?: IContainer
4748
) {
48-
super(
49-
initialInput,
50-
{
51-
defaultTitle: initialInput.title,
52-
defaultDescription: initialInput.description,
53-
},
54-
parent
55-
);
49+
super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent);
5650
}
5751

5852
public reportsEmbeddableLoad() {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import {
9+
Embeddable,
10+
type EmbeddableInput,
11+
type EmbeddableOutput,
12+
type IContainer,
13+
} from '@kbn/embeddable-plugin/public';
14+
import { type DataView } from '@kbn/data-views-plugin/common';
15+
import { type DataViewsContract } from '@kbn/data-views-plugin/public';
16+
import { firstValueFrom } from 'rxjs';
17+
import { type AnomalyDetectorService } from '../../application/services/anomaly_detector_service';
18+
19+
export type CommonInput = { jobIds: string[] } & EmbeddableInput;
20+
21+
export type CommonOutput = { indexPatterns?: DataView[] } & EmbeddableOutput;
22+
23+
export abstract class AnomalyDetectionEmbeddable<
24+
Input extends CommonInput,
25+
Output extends CommonOutput
26+
> extends Embeddable<Input, Output> {
27+
// Need to defer embeddable load in order to resolve data views
28+
deferEmbeddableLoad = true;
29+
30+
protected constructor(
31+
initialInput: Input,
32+
private anomalyDetectorService: AnomalyDetectorService,
33+
private dataViewsService: DataViewsContract,
34+
parent?: IContainer
35+
) {
36+
super(
37+
initialInput,
38+
{
39+
defaultTitle: initialInput.title,
40+
defaultDescription: initialInput.description,
41+
} as Output,
42+
parent
43+
);
44+
45+
this.initializeOutput(initialInput).finally(() => {
46+
this.setInitializationFinished();
47+
});
48+
}
49+
50+
protected async initializeOutput(initialInput: CommonInput) {
51+
const { jobIds } = initialInput;
52+
53+
try {
54+
const jobs = await firstValueFrom(this.anomalyDetectorService.getJobs$(jobIds));
55+
56+
// First get list of unique indices from the selected jobs
57+
const indices = new Set(jobs.map((j) => j.datafeed_config!.indices).flat());
58+
// Then find the data view assuming the data view title matches the index name
59+
const indexPatterns: Record<string, DataView> = {};
60+
for (const indexName of indices) {
61+
const response = await this.dataViewsService.find(`"${indexName}"`);
62+
const indexPattern = response.find((obj) =>
63+
obj.getIndexPattern().toLowerCase().includes(indexName.toLowerCase())
64+
);
65+
66+
if (indexPattern !== undefined) {
67+
indexPatterns[indexPattern.id!] = indexPattern;
68+
}
69+
}
70+
71+
this.updateOutput({
72+
...this.getOutput(),
73+
indexPatterns: Object.values(indexPatterns),
74+
});
75+
} catch (e) {
76+
// Unable to find and load data view but we can ignore the error
77+
// as we only load it to support the filter & query bar
78+
// the visualizations should still work correctly
79+
80+
// eslint-disable-next-line no-console
81+
console.error(`Unable to load data views for ${jobIds}`, e);
82+
}
83+
}
84+
}

x-pack/plugins/ml/public/embeddables/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface AnomalySwimlaneEmbeddableCustomOutput {
5858
perPage?: number;
5959
fromPage?: number;
6060
interval?: number;
61+
indexPatterns?: DataView[];
6162
}
6263

6364
export type AnomalySwimlaneEmbeddableOutput = EmbeddableOutput &

0 commit comments

Comments
 (0)