[8.11] [ML] AIOps: Fix Data View runtime fields support in Change point detection UI (#168249) (#168491)

# Backport

This will backport the following commits from `main` to `8.11`:
- [[ML] AIOps: Fix Data View runtime fields support in Change point
detection UI (#168249)](https://github.com/elastic/kibana/pull/168249)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Dima
Arnautov","email":"dmitrii.arnautov@elastic.co"},"sourceCommit":{"committedDate":"2023-10-10T14:55:03Z","message":"[ML]
AIOps: Fix Data View runtime fields support in Change point detection UI
(#168249)\n\n## Summary\r\n\r\nFixes
[#162212](https://github.com/elastic/kibana/issues/168212)\r\n\r\nIf a
Data View runtime field is used as a metric or split field, appends\r\na
`runtime_mappings` to the search request.\r\n\r\n### Checklist\r\n\r\n-
[ ] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"9f06e30a4e9addf1db6a3e78a11103d644e37995","branchLabelMapping":{"^v8.12.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix",":ml","Team:ML","Feature:ML/AIOps","v8.11.0","v8.12.0"],"number":168249,"url":"https://github.com/elastic/kibana/pull/168249","mergeCommit":{"message":"[ML]
AIOps: Fix Data View runtime fields support in Change point detection UI
(#168249)\n\n## Summary\r\n\r\nFixes
[#162212](https://github.com/elastic/kibana/issues/168212)\r\n\r\nIf a
Data View runtime field is used as a metric or split field, appends\r\na
`runtime_mappings` to the search request.\r\n\r\n### Checklist\r\n\r\n-
[ ] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"9f06e30a4e9addf1db6a3e78a11103d644e37995"}},"sourceBranch":"main","suggestedTargetBranches":["8.11"],"targetPullRequestStates":[{"branch":"8.11","label":"v8.11.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.12.0","labelRegex":"^v8.12.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/168249","number":168249,"mergeCommit":{"message":"[ML]
AIOps: Fix Data View runtime fields support in Change point detection UI
(#168249)\n\n## Summary\r\n\r\nFixes
[#162212](https://github.com/elastic/kibana/issues/168212)\r\n\r\nIf a
Data View runtime field is used as a metric or split field, appends\r\na
`runtime_mappings` to the search request.\r\n\r\n### Checklist\r\n\r\n-
[ ] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"9f06e30a4e9addf1db6a3e78a11103d644e37995"}}]}]
BACKPORT-->

Co-authored-by: Dima Arnautov <dmitrii.arnautov@elastic.co>
This commit is contained in:
Kibana Machine 2023-10-10 12:26:52 -04:00 committed by GitHub
parent 636a8339cf
commit fc9892165d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 44 deletions

View file

@ -9,12 +9,17 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { type QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { i18n } from '@kbn/i18n';
import { isDefined } from '@kbn/ml-is-defined';
import type {
MappingRuntimeFields,
SearchRequest,
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { useReload } from '../../hooks/use_reload';
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
import {
ChangePointAnnotation,
ChangePointDetectionRequestParams,
FieldConfig,
useChangePointDetectionControlsContext,
} from './change_point_detection_context';
import { useDataSource } from '../../hooks/use_data_source';
import { useCancellableSearch } from '../../hooks/use_cancellable_search';
@ -37,8 +42,9 @@ interface RequestOptions {
function getChangePointDetectionRequestBody(
{ index, fn, metricField, splitField, timeInterval, timeField, afterKey }: RequestOptions,
query: QueryDslQueryContainer
) {
query: QueryDslQueryContainer,
runtimeMappings: MappingRuntimeFields
): SearchRequest {
const timeSeriesAgg = {
over_time: {
date_histogram: {
@ -98,15 +104,14 @@ function getChangePointDetectionRequestBody(
: timeSeriesAgg;
return {
params: {
index,
size: 0,
body: {
...(query ? { query } : {}),
aggregations,
},
index,
size: 0,
body: {
...(query ? { query } : {}),
...(runtimeMappings ? { runtime_mappings: runtimeMappings } : {}),
aggregations,
},
};
} as SearchRequest;
}
export function useChangePointResults(
@ -120,7 +125,7 @@ export function useChangePointResults(
} = useAiopsAppContext();
const { dataView } = useDataSource();
const { splitFieldsOptions, metricFieldOptions } = useChangePointDetectionControlsContext();
const { refreshTimestamp: refresh } = useReload();
const [results, setResults] = useState<ChangePointAnnotation[]>([]);
@ -152,7 +157,23 @@ export function useChangePointResults(
return;
}
const requestPayload = getChangePointDetectionRequestBody(
const metricFieldDV = metricFieldOptions.find(
(option) => option.name === fieldConfig.metricField
);
const splitFieldDV = splitFieldsOptions.find(
(option) => option.name === fieldConfig.splitField
);
const runtimeMappings = {
...(metricFieldDV?.isRuntimeField
? { [metricFieldDV.name]: metricFieldDV.runtimeField! }
: {}),
...(splitFieldDV?.isRuntimeField
? { [splitFieldDV.name]: splitFieldDV.runtimeField! }
: {}),
} as MappingRuntimeFields;
const requestPayload: SearchRequest = getChangePointDetectionRequestBody(
{
index: dataView.getIndexPattern(),
fn: fieldConfig.fn,
@ -162,13 +183,14 @@ export function useChangePointResults(
splitField: fieldConfig.splitField,
afterKey,
},
query
query,
runtimeMappings
);
const result = await runRequest<
typeof requestPayload,
{ params: SearchRequest },
{ rawResponse: ChangePointAggResponse }
>(requestPayload);
>({ params: requestPayload });
if (result === null) {
setProgress(null);
@ -176,7 +198,7 @@ export function useChangePointResults(
}
const isFetchCompleted = !(
result.rawResponse.aggregations?.groupings?.after_key?.splitFieldTerm &&
isDefined(result.rawResponse.aggregations?.groupings?.after_key?.splitFieldTerm) &&
pageNumber < totalAggPages
);
@ -227,11 +249,11 @@ export function useChangePointResults(
if (
!isFetchCompleted &&
result.rawResponse.aggregations?.groupings?.after_key?.splitFieldTerm
isDefined(result.rawResponse.aggregations?.groupings?.after_key?.splitFieldTerm)
) {
await fetchResults(
pageNumber + 1,
result.rawResponse.aggregations.groupings.after_key.splitFieldTerm
result.rawResponse.aggregations.groupings.after_key!.splitFieldTerm
);
}
} catch (e) {
@ -243,17 +265,19 @@ export function useChangePointResults(
}
},
[
runRequest,
requestParams.interval,
requestParams.changePointType,
isSingleMetric,
totalAggPages,
dataView,
fieldConfig.fn,
fieldConfig.metricField,
fieldConfig.splitField,
requestParams.interval,
requestParams.changePointType,
query,
dataView,
totalAggPages,
metricFieldOptions,
splitFieldsOptions,
runRequest,
toasts,
isSingleMetric,
]
);

View file

@ -12,6 +12,7 @@ import type {
SearchResponseBody,
} from '@elastic/elasticsearch/lib/api/types';
import usePrevious from 'react-use/lib/usePrevious';
import { useChangePointDetectionControlsContext } from './change_point_detection_context';
import { useCancellableSearch } from '../../hooks/use_cancellable_search';
import { useDataSource } from '../../hooks/use_data_source';
@ -25,11 +26,19 @@ export function useSplitFieldCardinality(
query: QueryDslQueryContainer
) {
const prevSplitField = usePrevious(splitField);
const { splitFieldsOptions } = useChangePointDetectionControlsContext();
const [cardinality, setCardinality] = useState<number | null>(null);
const { dataView } = useDataSource();
const requestPayload = useMemo(() => {
const optionDefinition = splitFieldsOptions.find((option) => option.name === splitField);
let runtimeMappings = {};
if (optionDefinition?.isRuntimeField) {
runtimeMappings = {
runtime_mappings: { [optionDefinition.name]: optionDefinition.runtimeField },
};
}
return {
params: {
index: dataView.getIndexPattern(),
@ -43,10 +52,11 @@ export function useSplitFieldCardinality(
},
},
},
...runtimeMappings,
},
},
};
}, [splitField, dataView, query]);
}, [splitField, dataView, query, splitFieldsOptions]);
const { runRequest: getSplitFieldCardinality, cancelRequest } = useCancellableSearch();

View file

@ -12,9 +12,10 @@ import { useTimefilter } from '@kbn/ml-date-picker';
import { css } from '@emotion/react';
import useObservable from 'react-use/lib/useObservable';
import { ReloadContextProvider } from '../hooks/use_reload';
import type {
ChangePointAnnotation,
ChangePointDetectionRequestParams,
import {
type ChangePointAnnotation,
ChangePointDetectionControlsContextProvider,
type ChangePointDetectionRequestParams,
} from '../components/change_point_detection/change_point_detection_context';
import type {
EmbeddableChangePointChartInput,
@ -82,22 +83,24 @@ export const EmbeddableInputTracker: FC<EmbeddableInputTrackerProps> = ({
return (
<ReloadContextProvider reload$={resultObservable$}>
<DataSourceContextProvider dataViewId={input.dataViewId}>
<FilterQueryContextProvider timeRange={input.timeRange}>
<ChartGridEmbeddableWrapper
timeRange={input.timeRange}
fn={input.fn}
metricField={input.metricField}
splitField={input.splitField}
maxSeriesToPlot={input.maxSeriesToPlot}
dataViewId={input.dataViewId}
partitions={input.partitions}
onLoading={onLoading}
onRenderComplete={onRenderComplete}
onError={onError}
onChange={input.onChange}
emptyState={input.emptyState}
/>
</FilterQueryContextProvider>
<ChangePointDetectionControlsContextProvider>
<FilterQueryContextProvider timeRange={input.timeRange}>
<ChartGridEmbeddableWrapper
timeRange={input.timeRange}
fn={input.fn}
metricField={input.metricField}
splitField={input.splitField}
maxSeriesToPlot={input.maxSeriesToPlot}
dataViewId={input.dataViewId}
partitions={input.partitions}
onLoading={onLoading}
onRenderComplete={onRenderComplete}
onError={onError}
onChange={input.onChange}
emptyState={input.emptyState}
/>
</FilterQueryContextProvider>
</ChangePointDetectionControlsContextProvider>
</DataSourceContextProvider>
</ReloadContextProvider>
);