[ML] Fix Anomaly Detection wizard full time range chart blank with saved search containing runtime fields (#95700)
* [ML] Fix AD wizard full time range chart broken with saved search * [ML] Update runtimeMappingsSchema to be its own thing for better reuse * [ML] Remove undefined check
This commit is contained in:
parent
d6370f4e51
commit
a1bc9a57bd
|
@ -6,10 +6,7 @@
|
|||
*/
|
||||
|
||||
import { isPopulatedObject } from './object_utils';
|
||||
import {
|
||||
RUNTIME_FIELD_TYPES,
|
||||
RuntimeType,
|
||||
} from '../../../../../src/plugins/data/common/index_patterns';
|
||||
import { RUNTIME_FIELD_TYPES } from '../../../../../src/plugins/data/common/index_patterns';
|
||||
import type { RuntimeField, RuntimeMappings } from '../types/fields';
|
||||
|
||||
export function isRuntimeField(arg: unknown): arg is RuntimeField {
|
||||
|
@ -24,7 +21,7 @@ export function isRuntimeField(arg: unknown): arg is RuntimeField {
|
|||
Object.keys(arg.script).length === 1 &&
|
||||
arg.script.hasOwnProperty('source') &&
|
||||
typeof arg.script.source === 'string')))) &&
|
||||
RUNTIME_FIELD_TYPES.includes(arg.type as RuntimeType)
|
||||
RUNTIME_FIELD_TYPES.includes(arg.type)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ export function chartLoaderProvider(mlResultsService: MlResultsService) {
|
|||
job.data_counts.earliest_record_timestamp,
|
||||
job.data_counts.latest_record_timestamp,
|
||||
intervalMs,
|
||||
job.datafeed_config.runtime_mappings,
|
||||
// @ts-expect-error @elastic/elasticsearch Datafeed is missing indices_options
|
||||
job.datafeed_config.indices_options
|
||||
);
|
||||
|
|
|
@ -128,6 +128,7 @@ export class ChartLoader {
|
|||
start: number,
|
||||
end: number,
|
||||
intervalMs: number,
|
||||
runtimeMappings?: RuntimeMappings,
|
||||
indicesOptions?: IndicesOptions
|
||||
): Promise<LineChartPoint[]> {
|
||||
if (this._timeFieldName !== '') {
|
||||
|
@ -138,6 +139,7 @@ export class ChartLoader {
|
|||
start,
|
||||
end,
|
||||
intervalMs * 3,
|
||||
runtimeMappings,
|
||||
indicesOptions
|
||||
);
|
||||
if (resp.error !== undefined) {
|
||||
|
|
|
@ -55,6 +55,7 @@ export const CategorizationDetectorsSummary: FC = () => {
|
|||
jobCreator.start,
|
||||
jobCreator.end,
|
||||
chartInterval.getInterval().asMilliseconds(),
|
||||
jobCreator.runtimeMappings ?? undefined,
|
||||
// @ts-expect-error @elastic/elasticsearch Datafeed is missing indices_options
|
||||
jobCreator.datafeedConfig.indices_options
|
||||
);
|
||||
|
|
|
@ -48,6 +48,7 @@ export const TimeRangeStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep })
|
|||
jobCreator.start,
|
||||
jobCreator.end,
|
||||
chartInterval.getInterval().asMilliseconds(),
|
||||
jobCreator.runtimeMappings ?? undefined,
|
||||
// @ts-expect-error @elastic/elasticsearch Datafeed is missing indices_options
|
||||
jobCreator.datafeedConfig.indices_options
|
||||
);
|
||||
|
|
|
@ -44,6 +44,8 @@ import { JobId } from '../../../../../common/types/anomaly_detection_jobs';
|
|||
import { ML_PAGES } from '../../../../../common/constants/ml_url_generator';
|
||||
import { TIME_FORMAT } from '../../../../../common/constants/time_format';
|
||||
import { JobsAwaitingNodeWarning } from '../../../components/jobs_awaiting_node_warning';
|
||||
import { isPopulatedObject } from '../../../../../common/util/object_utils';
|
||||
import { RuntimeMappings } from '../../../../../common/types/fields';
|
||||
|
||||
export interface ModuleJobUI extends ModuleJob {
|
||||
datafeedResult?: DatafeedResponse;
|
||||
|
@ -133,10 +135,12 @@ export const Page: FC<PageProps> = ({ moduleId, existingGroupIds }) => {
|
|||
timeRange: TimeRange
|
||||
): Promise<TimeRange> => {
|
||||
if (useFullIndexData) {
|
||||
const runtimeMappings = indexPattern.getComputedFields().runtimeFields as RuntimeMappings;
|
||||
const { start, end } = await ml.getTimeFieldRange({
|
||||
index: indexPattern.title,
|
||||
timeFieldName: indexPattern.timeFieldName,
|
||||
query: combinedQuery,
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtimeMappings } : {}),
|
||||
});
|
||||
return {
|
||||
start: start.epoch,
|
||||
|
|
|
@ -10,6 +10,7 @@ import { MlApiServices } from '../ml_api_service';
|
|||
import type { AnomalyRecordDoc } from '../../../../common/types/anomalies';
|
||||
import { InfluencersFilterQuery } from '../../../../common/types/es_client';
|
||||
import { EntityField } from '../../../../common/util/anomaly_utils';
|
||||
import { RuntimeMappings } from '../../../../common/types/fields';
|
||||
|
||||
type RecordForInfluencer = AnomalyRecordDoc;
|
||||
export function resultsServiceProvider(
|
||||
|
@ -64,6 +65,7 @@ export function resultsServiceProvider(
|
|||
earliestMs: number,
|
||||
latestMs: number,
|
||||
intervalMs: number,
|
||||
runtimeMappings?: RuntimeMappings,
|
||||
indicesOptions?: IndicesOptions
|
||||
): Promise<any>;
|
||||
getEventDistributionData(
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
SWIM_LANE_DEFAULT_PAGE_SIZE,
|
||||
} from '../../explorer/explorer_constants';
|
||||
import { aggregationTypeTransform } from '../../../../common/util/anomaly_utils';
|
||||
import { isPopulatedObject } from '../../../../common/util/object_utils';
|
||||
|
||||
/**
|
||||
* Service for carrying out Elasticsearch queries to obtain data for the Ml Results dashboards.
|
||||
|
@ -1059,6 +1060,7 @@ export function resultsServiceProvider(mlApiServices) {
|
|||
earliestMs,
|
||||
latestMs,
|
||||
intervalMs,
|
||||
runtimeMappings,
|
||||
indicesOptions
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1109,6 +1111,12 @@ export function resultsServiceProvider(mlApiServices) {
|
|||
},
|
||||
},
|
||||
},
|
||||
// Runtime mappings only needed to support when query includes a runtime field
|
||||
// even though the default timeField can be a search time runtime field
|
||||
// because currently Kibana doesn't support that
|
||||
...(isPopulatedObject(runtimeMappings) && query
|
||||
? { runtime_mappings: runtimeMappings }
|
||||
: {}),
|
||||
},
|
||||
...(indicesOptions ?? {}),
|
||||
})
|
||||
|
|
|
@ -6,27 +6,13 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { isRuntimeField } from '../../../common/util/runtime_field_utils';
|
||||
import { runtimeMappingsSchema } from './runtime_mappings_schema';
|
||||
|
||||
export const indexPatternTitleSchema = schema.object({
|
||||
/** Title of the index pattern for which to return stats. */
|
||||
indexPatternTitle: schema.string(),
|
||||
});
|
||||
|
||||
const runtimeMappingsSchema = schema.maybe(
|
||||
schema.object(
|
||||
{},
|
||||
{
|
||||
unknowns: 'allow',
|
||||
validate: (v: object) => {
|
||||
if (Object.values(v).some((o) => !isRuntimeField(o))) {
|
||||
return 'Invalid runtime field';
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const dataVisualizerFieldHistogramsSchema = schema.object({
|
||||
/** Query to match documents in the index. */
|
||||
query: schema.any(),
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { indicesOptionsSchema } from './datafeeds_schema';
|
||||
import { runtimeMappingsSchema } from './runtime_mappings_schema';
|
||||
|
||||
export const getCardinalityOfFieldsSchema = schema.object({
|
||||
/** Index or indexes for which to return the time range. */
|
||||
|
@ -31,6 +32,6 @@ export const getTimeFieldRangeSchema = schema.object({
|
|||
/** Query to match documents in the index(es). */
|
||||
query: schema.maybe(schema.any()),
|
||||
/** Additional search options. */
|
||||
runtimeMappings: schema.maybe(schema.any()),
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
indicesOptions: indicesOptionsSchema,
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { anomalyDetectionJobSchema } from './anomaly_detectors_schema';
|
||||
import { datafeedConfigSchema, indicesOptionsSchema } from './datafeeds_schema';
|
||||
import { runtimeMappingsSchema } from './runtime_mappings_schema';
|
||||
|
||||
export const categorizationFieldExamplesSchema = {
|
||||
indexPatternTitle: schema.string(),
|
||||
|
@ -18,7 +19,7 @@ export const categorizationFieldExamplesSchema = {
|
|||
start: schema.number(),
|
||||
end: schema.number(),
|
||||
analyzer: schema.any(),
|
||||
runtimeMappings: schema.maybe(schema.any()),
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
indicesOptions: indicesOptionsSchema,
|
||||
};
|
||||
|
||||
|
@ -32,7 +33,7 @@ export const chartSchema = {
|
|||
aggFieldNamePairs: schema.arrayOf(schema.any()),
|
||||
splitFieldName: schema.maybe(schema.nullable(schema.string())),
|
||||
splitFieldValue: schema.maybe(schema.nullable(schema.string())),
|
||||
runtimeMappings: schema.maybe(schema.any()),
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
indicesOptions: indicesOptionsSchema,
|
||||
};
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { analysisConfigSchema, anomalyDetectionJobSchema } from './anomaly_detectors_schema';
|
||||
import { datafeedConfigSchema, indicesOptionsSchema } from './datafeeds_schema';
|
||||
import { runtimeMappingsSchema } from './runtime_mappings_schema';
|
||||
|
||||
export const estimateBucketSpanSchema = schema.object({
|
||||
aggTypes: schema.arrayOf(schema.nullable(schema.string())),
|
||||
|
@ -18,7 +19,7 @@ export const estimateBucketSpanSchema = schema.object({
|
|||
query: schema.any(),
|
||||
splitField: schema.maybe(schema.string()),
|
||||
timeField: schema.maybe(schema.string()),
|
||||
runtimeMappings: schema.maybe(schema.any()),
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
indicesOptions: indicesOptionsSchema,
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { isRuntimeField } from '../../../common/util/runtime_field_utils';
|
||||
|
||||
export const runtimeMappingsSchema = schema.maybe(
|
||||
schema.object(
|
||||
{},
|
||||
{
|
||||
unknowns: 'allow',
|
||||
validate: (v: object) => {
|
||||
if (Object.values(v).some((o) => !isRuntimeField(o))) {
|
||||
return 'Invalid runtime field';
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
Loading…
Reference in a new issue