mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Add search time runtime support for index based Data Visualizer (#95252)
* [ML] Add runtime support from index pattern for data viz
* [ML] move runtime mappings outside of aggregatableFields loop
* [ML] Change arg name to runtimeMappings
* [ML] Fix dv full time range broken
* [ML] Fix dv broken with time range
* [ML] Add better error handling/transparency
* [ML] Update to using estypes.RuntimeField
* [ML] Update to use some shared common functions between ml and transform
* Revert "[ML] Update to use some shared common functions between ml and transform"
This reverts commit ce813f01
* [ML] Disable context menu if no charts
This commit is contained in:
parent
3f86bab334
commit
587f83a859
9 changed files with 177 additions and 47 deletions
33
x-pack/plugins/ml/common/util/runtime_field_utils.ts
Normal file
33
x-pack/plugins/ml/common/util/runtime_field_utils.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { isPopulatedObject } from './object_utils';
|
||||
import {
|
||||
RUNTIME_FIELD_TYPES,
|
||||
RuntimeType,
|
||||
} from '../../../../../src/plugins/data/common/index_patterns';
|
||||
import type { RuntimeField, RuntimeMappings } from '../types/fields';
|
||||
|
||||
export function isRuntimeField(arg: unknown): arg is RuntimeField {
|
||||
return (
|
||||
isPopulatedObject(arg) &&
|
||||
((Object.keys(arg).length === 1 && arg.hasOwnProperty('type')) ||
|
||||
(Object.keys(arg).length === 2 &&
|
||||
arg.hasOwnProperty('type') &&
|
||||
arg.hasOwnProperty('script') &&
|
||||
(typeof arg.script === 'string' ||
|
||||
(isPopulatedObject(arg.script) &&
|
||||
Object.keys(arg.script).length === 1 &&
|
||||
arg.script.hasOwnProperty('source') &&
|
||||
typeof arg.script.source === 'string')))) &&
|
||||
RUNTIME_FIELD_TYPES.includes(arg.type as RuntimeType)
|
||||
);
|
||||
}
|
||||
|
||||
export function isRuntimeMappings(arg: unknown): arg is RuntimeMappings {
|
||||
return isPopulatedObject(arg) && Object.values(arg).every((d) => isRuntimeField(d));
|
||||
}
|
|
@ -13,6 +13,8 @@ import dateMath from '@elastic/datemath';
|
|||
import { getTimefilter, getToastNotifications } from '../../util/dependency_cache';
|
||||
import { ml, GetTimeFieldRangeResponse } from '../../services/ml_api_service';
|
||||
import { IndexPattern } from '../../../../../../../src/plugins/data/public';
|
||||
import { isPopulatedObject } from '../../../../common/util/object_utils';
|
||||
import { RuntimeMappings } from '../../../../common/types/fields';
|
||||
|
||||
export interface TimeRange {
|
||||
from: number;
|
||||
|
@ -25,10 +27,12 @@ export async function setFullTimeRange(
|
|||
): Promise<GetTimeFieldRangeResponse> {
|
||||
try {
|
||||
const timefilter = getTimefilter();
|
||||
const runtimeMappings = indexPattern.getComputedFields().runtimeFields as RuntimeMappings;
|
||||
const resp = await ml.getTimeFieldRange({
|
||||
index: indexPattern.title,
|
||||
timeFieldName: indexPattern.timeFieldName,
|
||||
query,
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtimeMappings } : {}),
|
||||
});
|
||||
timefilter.setTime({
|
||||
from: moment(resp.start.epoch).toISOString(),
|
||||
|
|
|
@ -18,23 +18,30 @@ import { DEFAULT_SAMPLER_SHARD_SIZE } from '../../../../../common/constants/fiel
|
|||
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import { FieldHistogramRequestConfig, FieldRequestConfig } from '../common';
|
||||
import { RuntimeMappings } from '../../../../../common/types/fields';
|
||||
import {
|
||||
ToastNotificationService,
|
||||
toastNotificationServiceProvider,
|
||||
} from '../../../services/toast_notification_service';
|
||||
|
||||
// Maximum number of examples to obtain for text type fields.
|
||||
const MAX_EXAMPLES_DEFAULT: number = 10;
|
||||
|
||||
export class DataLoader {
|
||||
private _indexPattern: IndexPattern;
|
||||
private _runtimeMappings: RuntimeMappings;
|
||||
private _indexPatternTitle: IndexPatternTitle = '';
|
||||
private _maxExamples: number = MAX_EXAMPLES_DEFAULT;
|
||||
private _toastNotifications: CoreSetup['notifications']['toasts'];
|
||||
private _toastNotificationsService: ToastNotificationService;
|
||||
|
||||
constructor(
|
||||
indexPattern: IndexPattern,
|
||||
toastNotifications: CoreSetup['notifications']['toasts']
|
||||
) {
|
||||
this._indexPattern = indexPattern;
|
||||
this._runtimeMappings = this._indexPattern.getComputedFields().runtimeFields as RuntimeMappings;
|
||||
this._indexPatternTitle = indexPattern.title;
|
||||
this._toastNotifications = toastNotifications;
|
||||
this._toastNotificationsService = toastNotificationServiceProvider(toastNotifications);
|
||||
}
|
||||
|
||||
async loadOverallData(
|
||||
|
@ -70,6 +77,7 @@ export class DataLoader {
|
|||
latest,
|
||||
aggregatableFields,
|
||||
nonAggregatableFields,
|
||||
runtimeMappings: this._runtimeMappings,
|
||||
});
|
||||
|
||||
return stats;
|
||||
|
@ -93,6 +101,7 @@ export class DataLoader {
|
|||
interval,
|
||||
fields,
|
||||
maxExamples: this._maxExamples,
|
||||
runtimeMappings: this._runtimeMappings,
|
||||
});
|
||||
|
||||
return stats;
|
||||
|
@ -108,6 +117,7 @@ export class DataLoader {
|
|||
query,
|
||||
fields,
|
||||
samplerShardSize,
|
||||
runtimeMappings: this._runtimeMappings,
|
||||
});
|
||||
|
||||
return stats;
|
||||
|
@ -115,7 +125,8 @@ export class DataLoader {
|
|||
|
||||
displayError(err: any) {
|
||||
if (err.statusCode === 500) {
|
||||
this._toastNotifications.addDanger(
|
||||
this._toastNotificationsService.displayErrorToast(
|
||||
err,
|
||||
i18n.translate('xpack.ml.datavisualizer.dataLoader.internalServerErrorMessage', {
|
||||
defaultMessage:
|
||||
'Error loading data in index {index}. {message}. ' +
|
||||
|
@ -127,7 +138,8 @@ export class DataLoader {
|
|||
})
|
||||
);
|
||||
} else {
|
||||
this._toastNotifications.addDanger(
|
||||
this._toastNotificationsService.displayErrorToast(
|
||||
err,
|
||||
i18n.translate('xpack.ml.datavisualizer.page.errorLoadingDataMessage', {
|
||||
defaultMessage: 'Error loading data in index {index}. {message}',
|
||||
values: {
|
||||
|
|
|
@ -38,10 +38,9 @@ import { FullTimeRangeSelector } from '../../components/full_time_range_selector
|
|||
import { mlTimefilterRefresh$ } from '../../services/timefilter_refresh_service';
|
||||
import { useMlContext } from '../../contexts/ml';
|
||||
import { kbnTypeToMLJobType } from '../../util/field_types_utils';
|
||||
import { useTimefilter } from '../../contexts/kibana';
|
||||
import { useNotifications, useTimefilter } from '../../contexts/kibana';
|
||||
import { timeBasedIndexCheck, getQueryFromSavedSearch } from '../../util/index_utils';
|
||||
import { getTimeBucketsFromCache } from '../../util/time_buckets';
|
||||
import { getToastNotifications } from '../../util/dependency_cache';
|
||||
import { usePageUrlState, useUrlState } from '../../util/url_state';
|
||||
import { ActionsPanel } from './components/actions_panel';
|
||||
import { SearchPanel } from './components/search_panel';
|
||||
|
@ -132,7 +131,8 @@ export const Page: FC = () => {
|
|||
autoRefreshSelector: true,
|
||||
});
|
||||
|
||||
const dataLoader = useMemo(() => new DataLoader(currentIndexPattern, getToastNotifications()), [
|
||||
const { toasts } = useNotifications();
|
||||
const dataLoader = useMemo(() => new DataLoader(currentIndexPattern, toasts), [
|
||||
currentIndexPattern,
|
||||
]);
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import {
|
|||
import { MlCapabilitiesResponse } from '../../../../common/types/capabilities';
|
||||
import { Calendar, CalendarId, UpdateCalendar } from '../../../../common/types/calendars';
|
||||
import { BucketSpanEstimatorData } from '../../../../common/types/job_service';
|
||||
import { RuntimeMappings } from '../../../../common/types/fields';
|
||||
import {
|
||||
Job,
|
||||
JobStats,
|
||||
|
@ -42,6 +41,7 @@ import {
|
|||
} from '../../datavisualizer/index_based/common';
|
||||
import { DataRecognizerConfigResponse, Module } from '../../../../common/types/modules';
|
||||
import { getHttp } from '../../util/dependency_cache';
|
||||
import type { RuntimeMappings } from '../../../../common/types/fields';
|
||||
|
||||
export interface MlInfoResponse {
|
||||
defaults: MlServerDefaults;
|
||||
|
@ -474,6 +474,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
|
|||
interval,
|
||||
fields,
|
||||
maxExamples,
|
||||
runtimeMappings,
|
||||
}: {
|
||||
indexPatternTitle: string;
|
||||
query: any;
|
||||
|
@ -484,6 +485,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
|
|||
interval?: number;
|
||||
fields?: FieldRequestConfig[];
|
||||
maxExamples?: number;
|
||||
runtimeMappings?: RuntimeMappings;
|
||||
}) {
|
||||
const body = JSON.stringify({
|
||||
query,
|
||||
|
@ -494,6 +496,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
|
|||
interval,
|
||||
fields,
|
||||
maxExamples,
|
||||
runtimeMappings,
|
||||
});
|
||||
|
||||
return httpService.http<any>({
|
||||
|
@ -508,16 +511,19 @@ export function mlApiServicesProvider(httpService: HttpService) {
|
|||
query,
|
||||
fields,
|
||||
samplerShardSize,
|
||||
runtimeMappings,
|
||||
}: {
|
||||
indexPatternTitle: string;
|
||||
query: any;
|
||||
fields: FieldHistogramRequestConfig[];
|
||||
samplerShardSize?: number;
|
||||
runtimeMappings?: RuntimeMappings;
|
||||
}) {
|
||||
const body = JSON.stringify({
|
||||
query,
|
||||
fields,
|
||||
samplerShardSize,
|
||||
runtimeMappings,
|
||||
});
|
||||
|
||||
return httpService.http<any>({
|
||||
|
@ -536,6 +542,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
|
|||
samplerShardSize,
|
||||
aggregatableFields,
|
||||
nonAggregatableFields,
|
||||
runtimeMappings,
|
||||
}: {
|
||||
indexPatternTitle: string;
|
||||
query: any;
|
||||
|
@ -545,6 +552,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
|
|||
samplerShardSize?: number;
|
||||
aggregatableFields: string[];
|
||||
nonAggregatableFields: string[];
|
||||
runtimeMappings?: RuntimeMappings;
|
||||
}) {
|
||||
const body = JSON.stringify({
|
||||
query,
|
||||
|
@ -554,6 +562,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
|
|||
samplerShardSize,
|
||||
aggregatableFields,
|
||||
nonAggregatableFields,
|
||||
runtimeMappings,
|
||||
});
|
||||
|
||||
return httpService.http<any>({
|
||||
|
|
|
@ -211,7 +211,7 @@ const getAggIntervals = async (
|
|||
query,
|
||||
aggs: buildSamplerAggregation(minMaxAggs, samplerShardSize),
|
||||
size: 0,
|
||||
...(runtimeMappings !== undefined ? { runtime_mappings: runtimeMappings } : {}),
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -297,7 +297,7 @@ export const getHistogramsForFields = async (
|
|||
query,
|
||||
aggs: buildSamplerAggregation(chartDataAggs, samplerShardSize),
|
||||
size: 0,
|
||||
...(runtimeMappings !== undefined ? { runtime_mappings: runtimeMappings } : {}),
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -369,7 +369,8 @@ export class DataVisualizer {
|
|||
samplerShardSize: number,
|
||||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number
|
||||
latestMs: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
) {
|
||||
const stats = {
|
||||
totalCount: 0,
|
||||
|
@ -400,7 +401,9 @@ export class DataVisualizer {
|
|||
samplerShardSize,
|
||||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs
|
||||
latestMs,
|
||||
undefined,
|
||||
runtimeMappings
|
||||
);
|
||||
|
||||
// Total count will be returned with each batch of fields. Just overwrite.
|
||||
|
@ -420,7 +423,8 @@ export class DataVisualizer {
|
|||
field,
|
||||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs
|
||||
latestMs,
|
||||
runtimeMappings
|
||||
);
|
||||
|
||||
const fieldData: FieldData = {
|
||||
|
@ -447,14 +451,16 @@ export class DataVisualizer {
|
|||
indexPatternTitle: string,
|
||||
query: any,
|
||||
fields: HistogramField[],
|
||||
samplerShardSize: number
|
||||
samplerShardSize: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
): Promise<any> {
|
||||
return await getHistogramsForFields(
|
||||
this._client,
|
||||
indexPatternTitle,
|
||||
query,
|
||||
fields,
|
||||
samplerShardSize
|
||||
samplerShardSize,
|
||||
runtimeMappings
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -470,7 +476,8 @@ export class DataVisualizer {
|
|||
earliestMs: number,
|
||||
latestMs: number,
|
||||
intervalMs: number,
|
||||
maxExamples: number
|
||||
maxExamples: number,
|
||||
runtimeMappings: RuntimeMappings
|
||||
): Promise<BatchStats[]> {
|
||||
// Batch up fields by type, getting stats for multiple fields at a time.
|
||||
const batches: Field[][] = [];
|
||||
|
@ -516,7 +523,8 @@ export class DataVisualizer {
|
|||
samplerShardSize,
|
||||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs
|
||||
latestMs,
|
||||
runtimeMappings
|
||||
);
|
||||
} else {
|
||||
// Will only ever be one document count card,
|
||||
|
@ -527,7 +535,8 @@ export class DataVisualizer {
|
|||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
intervalMs
|
||||
intervalMs,
|
||||
runtimeMappings
|
||||
);
|
||||
batchStats.push(stats);
|
||||
}
|
||||
|
@ -541,7 +550,8 @@ export class DataVisualizer {
|
|||
samplerShardSize,
|
||||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs
|
||||
latestMs,
|
||||
runtimeMappings
|
||||
);
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.DATE:
|
||||
|
@ -552,7 +562,8 @@ export class DataVisualizer {
|
|||
samplerShardSize,
|
||||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs
|
||||
latestMs,
|
||||
runtimeMappings
|
||||
);
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.BOOLEAN:
|
||||
|
@ -563,7 +574,8 @@ export class DataVisualizer {
|
|||
samplerShardSize,
|
||||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs
|
||||
latestMs,
|
||||
runtimeMappings
|
||||
);
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.TEXT:
|
||||
|
@ -579,7 +591,8 @@ export class DataVisualizer {
|
|||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
maxExamples
|
||||
maxExamples,
|
||||
runtimeMappings
|
||||
);
|
||||
batchStats.push(stats);
|
||||
})
|
||||
|
@ -602,7 +615,8 @@ export class DataVisualizer {
|
|||
timeFieldName: string,
|
||||
earliestMs?: number,
|
||||
latestMs?: number,
|
||||
datafeedConfig?: Datafeed
|
||||
datafeedConfig?: Datafeed,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
) {
|
||||
const index = indexPatternTitle;
|
||||
const size = 0;
|
||||
|
@ -612,7 +626,14 @@ export class DataVisualizer {
|
|||
// Value count aggregation faster way of checking if field exists than using
|
||||
// filter aggregation with exists query.
|
||||
const aggs: Aggs = datafeedAggregations !== undefined ? { ...datafeedAggregations } : {};
|
||||
const runtimeMappings: { runtime_mappings?: RuntimeMappings } = {};
|
||||
|
||||
// Combine runtime mappings from the index pattern as well as the datafeed
|
||||
const combinedRuntimeMappings: RuntimeMappings = {
|
||||
...(isPopulatedObject(runtimeMappings) ? runtimeMappings : {}),
|
||||
...(isPopulatedObject(datafeedConfig) && isPopulatedObject(datafeedConfig.runtime_mappings)
|
||||
? datafeedConfig.runtime_mappings
|
||||
: {}),
|
||||
};
|
||||
|
||||
aggregatableFields.forEach((field, i) => {
|
||||
const safeFieldName = getSafeAggregationName(field, i);
|
||||
|
@ -629,9 +650,6 @@ export class DataVisualizer {
|
|||
cardinalityField = {
|
||||
cardinality: { field },
|
||||
};
|
||||
if (datafeedConfig !== undefined && isPopulatedObject(datafeedConfig?.runtime_mappings)) {
|
||||
runtimeMappings.runtime_mappings = datafeedConfig.runtime_mappings;
|
||||
}
|
||||
}
|
||||
aggs[`${safeFieldName}_cardinality`] = cardinalityField;
|
||||
});
|
||||
|
@ -643,7 +661,9 @@ export class DataVisualizer {
|
|||
},
|
||||
},
|
||||
...(isPopulatedObject(aggs) ? { aggs: buildSamplerAggregation(aggs, samplerShardSize) } : {}),
|
||||
...runtimeMappings,
|
||||
...(isPopulatedObject(combinedRuntimeMappings)
|
||||
? { runtime_mappings: combinedRuntimeMappings }
|
||||
: {}),
|
||||
};
|
||||
|
||||
const { body } = await this._asCurrentUser.search({
|
||||
|
@ -720,7 +740,8 @@ export class DataVisualizer {
|
|||
field: string,
|
||||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number
|
||||
latestMs: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
) {
|
||||
const index = indexPatternTitle;
|
||||
const size = 0;
|
||||
|
@ -732,6 +753,7 @@ export class DataVisualizer {
|
|||
filter: filterCriteria,
|
||||
},
|
||||
},
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
};
|
||||
filterCriteria.push({ exists: { field } });
|
||||
|
||||
|
@ -750,7 +772,8 @@ export class DataVisualizer {
|
|||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number,
|
||||
intervalMs: number
|
||||
intervalMs: number,
|
||||
runtimeMappings: RuntimeMappings
|
||||
): Promise<DocumentCountStats> {
|
||||
const index = indexPatternTitle;
|
||||
const size = 0;
|
||||
|
@ -776,6 +799,7 @@ export class DataVisualizer {
|
|||
},
|
||||
},
|
||||
aggs,
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
};
|
||||
|
||||
const { body } = await this._asCurrentUser.search({
|
||||
|
@ -810,7 +834,8 @@ export class DataVisualizer {
|
|||
samplerShardSize: number,
|
||||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number
|
||||
latestMs: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
) {
|
||||
const index = indexPatternTitle;
|
||||
const size = 0;
|
||||
|
@ -879,6 +904,7 @@ export class DataVisualizer {
|
|||
},
|
||||
},
|
||||
aggs: buildSamplerAggregation(aggs, samplerShardSize),
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
};
|
||||
|
||||
const { body } = await this._asCurrentUser.search({
|
||||
|
@ -958,7 +984,8 @@ export class DataVisualizer {
|
|||
samplerShardSize: number,
|
||||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number
|
||||
latestMs: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
) {
|
||||
const index = indexPatternTitle;
|
||||
const size = 0;
|
||||
|
@ -1000,6 +1027,7 @@ export class DataVisualizer {
|
|||
},
|
||||
},
|
||||
aggs: buildSamplerAggregation(aggs, samplerShardSize),
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
};
|
||||
|
||||
const { body } = await this._asCurrentUser.search({
|
||||
|
@ -1048,7 +1076,8 @@ export class DataVisualizer {
|
|||
samplerShardSize: number,
|
||||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number
|
||||
latestMs: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
) {
|
||||
const index = indexPatternTitle;
|
||||
const size = 0;
|
||||
|
@ -1074,6 +1103,7 @@ export class DataVisualizer {
|
|||
},
|
||||
},
|
||||
aggs: buildSamplerAggregation(aggs, samplerShardSize),
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
};
|
||||
|
||||
const { body } = await this._asCurrentUser.search({
|
||||
|
@ -1114,7 +1144,8 @@ export class DataVisualizer {
|
|||
samplerShardSize: number,
|
||||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number
|
||||
latestMs: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
) {
|
||||
const index = indexPatternTitle;
|
||||
const size = 0;
|
||||
|
@ -1141,6 +1172,7 @@ export class DataVisualizer {
|
|||
},
|
||||
},
|
||||
aggs: buildSamplerAggregation(aggs, samplerShardSize),
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
};
|
||||
|
||||
const { body } = await this._asCurrentUser.search({
|
||||
|
@ -1182,7 +1214,8 @@ export class DataVisualizer {
|
|||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number,
|
||||
maxExamples: number
|
||||
maxExamples: number,
|
||||
runtimeMappings?: RuntimeMappings
|
||||
): Promise<FieldExamples> {
|
||||
const index = indexPatternTitle;
|
||||
|
||||
|
@ -1204,6 +1237,7 @@ export class DataVisualizer {
|
|||
filter: filterCriteria,
|
||||
},
|
||||
},
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
};
|
||||
|
||||
const { body } = await this._asCurrentUser.search({
|
||||
|
|
|
@ -15,6 +15,7 @@ import { isValidAggregationField } from '../../../common/util/validation_utils';
|
|||
import { getDatafeedAggregations } from '../../../common/util/datafeed_utils';
|
||||
import { Datafeed, IndicesOptions } from '../../../common/types/anomaly_detection_jobs';
|
||||
import { RuntimeMappings } from '../../../common/types/fields';
|
||||
import { isPopulatedObject } from '../../../common/util/object_utils';
|
||||
|
||||
/**
|
||||
* Service for carrying out queries to obtain data
|
||||
|
@ -243,7 +244,7 @@ export function fieldsServiceProvider({ asCurrentUser }: IScopedClusterClient) {
|
|||
},
|
||||
},
|
||||
},
|
||||
...(runtimeMappings !== undefined ? { runtime_mappings: runtimeMappings } : {}),
|
||||
...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}),
|
||||
},
|
||||
...(indicesOptions ?? {}),
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
indexPatternTitleSchema,
|
||||
} from './schemas/data_visualizer_schema';
|
||||
import { RouteInitialization } from '../types';
|
||||
import { RuntimeMappings } from '../../common/types/fields';
|
||||
|
||||
function getOverallStats(
|
||||
client: IScopedClusterClient,
|
||||
|
@ -26,7 +27,8 @@ function getOverallStats(
|
|||
samplerShardSize: number,
|
||||
timeFieldName: string,
|
||||
earliestMs: number,
|
||||
latestMs: number
|
||||
latestMs: number,
|
||||
runtimeMappings: RuntimeMappings
|
||||
) {
|
||||
const dv = new DataVisualizer(client);
|
||||
return dv.getOverallStats(
|
||||
|
@ -37,7 +39,8 @@ function getOverallStats(
|
|||
samplerShardSize,
|
||||
timeFieldName,
|
||||
earliestMs,
|
||||
latestMs
|
||||
latestMs,
|
||||
runtimeMappings
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -51,7 +54,8 @@ function getStatsForFields(
|
|||
earliestMs: number,
|
||||
latestMs: number,
|
||||
interval: number,
|
||||
maxExamples: number
|
||||
maxExamples: number,
|
||||
runtimeMappings: RuntimeMappings
|
||||
) {
|
||||
const dv = new DataVisualizer(client);
|
||||
return dv.getStatsForFields(
|
||||
|
@ -63,7 +67,8 @@ function getStatsForFields(
|
|||
earliestMs,
|
||||
latestMs,
|
||||
interval,
|
||||
maxExamples
|
||||
maxExamples,
|
||||
runtimeMappings
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -72,10 +77,17 @@ function getHistogramsForFields(
|
|||
indexPatternTitle: string,
|
||||
query: any,
|
||||
fields: HistogramField[],
|
||||
samplerShardSize: number
|
||||
samplerShardSize: number,
|
||||
runtimeMappings: RuntimeMappings
|
||||
) {
|
||||
const dv = new DataVisualizer(client);
|
||||
return dv.getHistogramsForFields(indexPatternTitle, query, fields, samplerShardSize);
|
||||
return dv.getHistogramsForFields(
|
||||
indexPatternTitle,
|
||||
query,
|
||||
fields,
|
||||
samplerShardSize,
|
||||
runtimeMappings
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,7 +121,7 @@ export function dataVisualizerRoutes({ router, routeGuard }: RouteInitialization
|
|||
try {
|
||||
const {
|
||||
params: { indexPatternTitle },
|
||||
body: { query, fields, samplerShardSize },
|
||||
body: { query, fields, samplerShardSize, runtimeMappings },
|
||||
} = request;
|
||||
|
||||
const results = await getHistogramsForFields(
|
||||
|
@ -117,7 +129,8 @@ export function dataVisualizerRoutes({ router, routeGuard }: RouteInitialization
|
|||
indexPatternTitle,
|
||||
query,
|
||||
fields,
|
||||
samplerShardSize
|
||||
samplerShardSize,
|
||||
runtimeMappings
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
|
@ -165,9 +178,9 @@ export function dataVisualizerRoutes({ router, routeGuard }: RouteInitialization
|
|||
latest,
|
||||
interval,
|
||||
maxExamples,
|
||||
runtimeMappings,
|
||||
},
|
||||
} = request;
|
||||
|
||||
const results = await getStatsForFields(
|
||||
client,
|
||||
indexPatternTitle,
|
||||
|
@ -178,7 +191,8 @@ export function dataVisualizerRoutes({ router, routeGuard }: RouteInitialization
|
|||
earliest,
|
||||
latest,
|
||||
interval,
|
||||
maxExamples
|
||||
maxExamples,
|
||||
runtimeMappings
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
|
@ -229,6 +243,7 @@ export function dataVisualizerRoutes({ router, routeGuard }: RouteInitialization
|
|||
timeFieldName,
|
||||
earliest,
|
||||
latest,
|
||||
runtimeMappings,
|
||||
},
|
||||
} = request;
|
||||
|
||||
|
@ -241,7 +256,8 @@ export function dataVisualizerRoutes({ router, routeGuard }: RouteInitialization
|
|||
samplerShardSize,
|
||||
timeFieldName,
|
||||
earliest,
|
||||
latest
|
||||
latest,
|
||||
runtimeMappings
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
|
|
|
@ -6,12 +6,27 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { isRuntimeField } from '../../../common/util/runtime_field_utils';
|
||||
|
||||
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(),
|
||||
|
@ -19,6 +34,8 @@ export const dataVisualizerFieldHistogramsSchema = schema.object({
|
|||
fields: schema.arrayOf(schema.any()),
|
||||
/** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */
|
||||
samplerShardSize: schema.number(),
|
||||
/** Optional search time runtime mappings */
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
});
|
||||
|
||||
export const dataVisualizerFieldStatsSchema = schema.object({
|
||||
|
@ -37,6 +54,8 @@ export const dataVisualizerFieldStatsSchema = schema.object({
|
|||
interval: schema.maybe(schema.number()),
|
||||
/** Maximum number of examples to return for text type fields. */
|
||||
maxExamples: schema.number(),
|
||||
/** Optional search time runtime mappings */
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
});
|
||||
|
||||
export const dataVisualizerOverallStatsSchema = schema.object({
|
||||
|
@ -54,4 +73,6 @@ export const dataVisualizerOverallStatsSchema = schema.object({
|
|||
earliest: schema.maybe(schema.number()),
|
||||
/** Latest timestamp for search, as epoch ms (optional). */
|
||||
latest: schema.maybe(schema.number()),
|
||||
/** Optional search time runtime mappings */
|
||||
runtimeMappings: runtimeMappingsSchema,
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue