mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] AIOps: Fix Data View runtime fields support in Change point detection UI (#168249)
## Summary Fixes [#162212](https://github.com/elastic/kibana/issues/168212) If a Data View runtime field is used as a metric or split field, appends a `runtime_mappings` to the search request. ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
e3d9f3d62e
commit
9f06e30a4e
3 changed files with 81 additions and 44 deletions
|
@ -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,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue