Skip to content

Commit

Permalink
[ML] Add search time runtime support for index based Data Visualizer (#…
Browse files Browse the repository at this point in the history
…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 ce813f0

* [ML] Disable context menu if no charts
  • Loading branch information
qn895 authored Mar 29, 2021
1 parent 3f86bab commit 587f83a
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 47 deletions.
33 changes: 33 additions & 0 deletions x-pack/plugins/ml/common/util/runtime_field_utils.ts
Original file line number Diff line number Diff line change
@@ -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));
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -70,6 +77,7 @@ export class DataLoader {
latest,
aggregatableFields,
nonAggregatableFields,
runtimeMappings: this._runtimeMappings,
});

return stats;
Expand All @@ -93,6 +101,7 @@ export class DataLoader {
interval,
fields,
maxExamples: this._maxExamples,
runtimeMappings: this._runtimeMappings,
});

return stats;
Expand All @@ -108,14 +117,16 @@ export class DataLoader {
query,
fields,
samplerShardSize,
runtimeMappings: this._runtimeMappings,
});

return stats;
}

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}. ' +
Expand All @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -474,6 +474,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
interval,
fields,
maxExamples,
runtimeMappings,
}: {
indexPatternTitle: string;
query: any;
Expand All @@ -484,6 +485,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
interval?: number;
fields?: FieldRequestConfig[];
maxExamples?: number;
runtimeMappings?: RuntimeMappings;
}) {
const body = JSON.stringify({
query,
Expand All @@ -494,6 +496,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
interval,
fields,
maxExamples,
runtimeMappings,
});

return httpService.http<any>({
Expand All @@ -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>({
Expand All @@ -536,6 +542,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
samplerShardSize,
aggregatableFields,
nonAggregatableFields,
runtimeMappings,
}: {
indexPatternTitle: string;
query: any;
Expand All @@ -545,6 +552,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
samplerShardSize?: number;
aggregatableFields: string[];
nonAggregatableFields: string[];
runtimeMappings?: RuntimeMappings;
}) {
const body = JSON.stringify({
query,
Expand All @@ -554,6 +562,7 @@ export function mlApiServicesProvider(httpService: HttpService) {
samplerShardSize,
aggregatableFields,
nonAggregatableFields,
runtimeMappings,
});

return httpService.http<any>({
Expand Down
Loading

0 comments on commit 587f83a

Please sign in to comment.