Skip to content

Commit

Permalink
[ML] Get rid of imports from 'elasticsearch'.
Browse files Browse the repository at this point in the history
  • Loading branch information
walterra committed Mar 29, 2021
1 parent 84adfe5 commit cbba256
Show file tree
Hide file tree
Showing 43 changed files with 243 additions and 182 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

export { HitsTotalRelation, SearchResponse7, HITS_TOTAL_RELATION } from './types/es_client';
export { ES_CLIENT_TOTAL_HITS_RELATION } from './types/es_client';
export { ChartData } from './types/field_histograms';
export { ANOMALY_SEVERITY, ANOMALY_THRESHOLD, SEVERITY_COLORS } from './constants/anomalies';
export { getSeverityColor, getSeverityType } from './util/anomaly_utils';
Expand Down
33 changes: 12 additions & 21 deletions x-pack/plugins/ml/common/types/es_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,24 @@
* 2.0.
*/

import type { SearchResponse, ShardsResponse } from 'elasticsearch';
import { estypes } from '@elastic/elasticsearch';

import { buildEsQuery } from '../../../../../src/plugins/data/common/es_query/es_query';
import type { DslQuery } from '../../../../../src/plugins/data/common/es_query/kuery';
import type { JsonObject } from '../../../../../src/plugins/kibana_utils/common';

export const HITS_TOTAL_RELATION = {
import { isPopulatedObject } from '../util/object_utils';

export function isMultiBucketAggregate(arg: unknown): arg is estypes.MultiBucketAggregate {
return isPopulatedObject(arg, ['buckets']);
}

export const ES_CLIENT_TOTAL_HITS_RELATION: Record<
Uppercase<estypes.TotalHitsRelation>,
estypes.TotalHitsRelation
> = {
EQ: 'eq',
GTE: 'gte',
} as const;
export type HitsTotalRelation = typeof HITS_TOTAL_RELATION[keyof typeof HITS_TOTAL_RELATION];

// The types specified in `@types/elasticsearch` are out of date and still have `total: number`.
interface SearchResponse7Hits<T> {
hits: SearchResponse<T>['hits']['hits'];
max_score: number;
total: {
value: number;
relation: HitsTotalRelation;
};
}
export interface SearchResponse7<T = any> {
took: number;
timed_out: boolean;
_scroll_id?: string;
_shards: ShardsResponse;
hits: SearchResponse7Hits<T>;
aggregations?: any;
}

export type InfluencersFilterQuery = ReturnType<typeof buildEsQuery> | DslQuery | JsonObject;
8 changes: 2 additions & 6 deletions x-pack/plugins/ml/common/types/feature_importance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,11 @@ export function isRegressionTotalFeatureImportance(
export function isClassificationFeatureImportanceBaseline(
baselineData: any
): baselineData is ClassificationFeatureImportanceBaseline {
return (
isPopulatedObject(baselineData) &&
baselineData.hasOwnProperty('classes') &&
Array.isArray(baselineData.classes)
);
return isPopulatedObject(baselineData, ['classes']) && Array.isArray(baselineData.classes);
}

export function isRegressionFeatureImportanceBaseline(
baselineData: any
): baselineData is RegressionFeatureImportanceBaseline {
return isPopulatedObject(baselineData) && baselineData.hasOwnProperty('baseline');
return isPopulatedObject(baselineData, ['baseline']);
}
32 changes: 32 additions & 0 deletions x-pack/plugins/ml/common/util/object_utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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';

describe('object_utils', () => {
test('isPopulatedObject()', () => {
expect(isPopulatedObject(0)).toBe(false);
expect(isPopulatedObject('')).toBe(false);
expect(isPopulatedObject(null)).toBe(false);
expect(isPopulatedObject({})).toBe(false);
expect(isPopulatedObject({ attribute: 'value' })).toBe(true);
expect(isPopulatedObject({ attribute: 'value' }, ['otherAttribute'])).toBe(false);
expect(isPopulatedObject({ attribute: 'value' }, ['attribute'])).toBe(true);
expect(
isPopulatedObject({ attribute1: 'value1', attribute2: 'value2' }, [
'attribute1',
'attribute2',
])
).toBe(true);
expect(
isPopulatedObject({ attribute1: 'value1', attribute2: 'value2' }, [
'attribute1',
'otherAttribute',
])
).toBe(true);
});
});
13 changes: 11 additions & 2 deletions x-pack/plugins/ml/common/util/object_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
* 2.0.
*/

export const isPopulatedObject = <T = Record<string, any>>(arg: any): arg is T => {
return typeof arg === 'object' && arg !== null && Object.keys(arg).length > 0;
export const isPopulatedObject = <T = Record<string, unknown>>(
arg: unknown,
requiredAttributes: string[] = []
): arg is T => {
return (
typeof arg === 'object' &&
arg !== null &&
Object.keys(arg).length > 0 &&
(requiredAttributes.length === 0 ||
requiredAttributes.every((d) => ({}.hasOwnProperty.call(arg, d))))
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { Dispatch, SetStateAction } from 'react';

import { estypes } from '@elastic/elasticsearch';
import {
EuiDataGridCellValueElementProps,
EuiDataGridPaginationProps,
Expand All @@ -15,7 +16,6 @@ import {
} from '@elastic/eui';

import { Dictionary } from '../../../../common/types/common';
import { HitsTotalRelation } from '../../../../common/types/es_client';
import { ChartData } from '../../../../common/types/field_histograms';

import { INDEX_STATUS } from '../../data_frame_analytics/common/analytics';
Expand All @@ -27,7 +27,7 @@ export type DataGridItem = Record<string, any>;

// `undefined` is used to indicate a non-initialized state.
export type ChartsVisible = boolean | undefined;
export type RowCountRelation = HitsTotalRelation | undefined;
export type RowCountRelation = estypes.TotalHitsRelation | undefined;

export type IndexPagination = Pick<EuiDataGridPaginationProps, 'pageIndex' | 'pageSize'>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';

import { EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui';

import { HITS_TOTAL_RELATION } from '../../../../common/types/es_client';
import { ES_CLIENT_TOTAL_HITS_RELATION } from '../../../../common/types/es_client';
import { ChartData } from '../../../../common/types/field_histograms';

import { INDEX_STATUS } from '../../data_frame_analytics/common';
Expand Down Expand Up @@ -146,7 +146,7 @@ export const useDataGrid = (
if (chartsVisible === undefined && rowCount > 0 && rowCountRelation !== undefined) {
setChartsVisible(
rowCount <= COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED &&
rowCountRelation !== HITS_TOTAL_RELATION.GTE
rowCountRelation !== ES_CLIENT_TOTAL_HITS_RELATION.GTE
);
}
}, [chartsVisible, rowCount, rowCountRelation]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import React, { useMemo, useEffect, useState, FC } from 'react';

import { estypes } from '@elastic/elasticsearch';

import {
EuiCallOut,
EuiComboBox,
Expand All @@ -24,7 +26,6 @@ import { i18n } from '@kbn/i18n';

import { extractErrorMessage } from '../../../../common';
import { stringHash } from '../../../../common/util/string_utils';
import type { SearchResponse7 } from '../../../../common/types/es_client';
import type { ResultsSearchQuery } from '../../data_frame_analytics/common/analytics';

import { useMlApiContext } from '../../contexts/kibana';
Expand Down Expand Up @@ -184,7 +185,7 @@ export const ScatterplotMatrix: FC<ScatterplotMatrixProps> = ({
}
: searchQuery;

const resp: SearchResponse7 = await esSearch({
const resp: estypes.SearchResponse = await esSearch({
index,
body: {
fields: queryFields,
Expand All @@ -198,7 +199,7 @@ export const ScatterplotMatrix: FC<ScatterplotMatrixProps> = ({
if (!options.didCancel) {
const items = resp.hits.hits
.map((d) =>
getProcessedFields(d.fields, (key: string) =>
getProcessedFields(d.fields ?? {}, (key: string) =>
key.startsWith(`${resultsField}.feature_importance`)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import type { SearchResponse7 } from '../../../../common/types/es_client';
import type { estypes } from '@elastic/elasticsearch';
import { extractErrorMessage } from '../../../../common/util/errors';

import { EsSorting, UseDataGridReturnType, getProcessedFields } from '../../components/data_grid';
Expand Down Expand Up @@ -51,7 +51,7 @@ export const getIndexData = async (

const { pageIndex, pageSize } = pagination;
// TODO: remove results_field from `fields` when possible
const resp: SearchResponse7 = await ml.esSearch({
const resp: estypes.SearchResponse = await ml.esSearch({
index: jobConfig.dest.index,
body: {
fields: ['*'],
Expand All @@ -64,11 +64,15 @@ export const getIndexData = async (
});

if (!options.didCancel) {
setRowCount(resp.hits.total.value);
setRowCountRelation(resp.hits.total.relation);
setRowCount(typeof resp.hits.total === 'number' ? resp.hits.total : resp.hits.total.value);
setRowCountRelation(
typeof resp.hits.total === 'number'
? ('eq' as estypes.TotalHitsRelation)
: resp.hits.total.relation
);
setTableItems(
resp.hits.hits.map((d) =>
getProcessedFields(d.fields, (key: string) =>
getProcessedFields(d.fields ?? {}, (key: string) =>
key.startsWith(`${jobConfig.dest.results_field}.feature_importance`)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { useEffect, useMemo } from 'react';

import { estypes } from '@elastic/elasticsearch';
import { EuiDataGridColumn } from '@elastic/eui';

import { CoreSetup } from 'src/core/public';
Expand All @@ -26,13 +27,12 @@ import {
UseIndexDataReturnType,
getProcessedFields,
} from '../../../../components/data_grid';
import type { SearchResponse7 } from '../../../../../../common/types/es_client';
import { extractErrorMessage } from '../../../../../../common/util/errors';
import { INDEX_STATUS } from '../../../common/analytics';
import { ml } from '../../../../services/ml_api_service';
import { getRuntimeFieldsMapping } from '../../../../components/data_grid/common';

type IndexSearchResponse = SearchResponse7;
type IndexSearchResponse = estypes.SearchResponse;

export const useIndexData = (
indexPattern: IndexPattern,
Expand Down Expand Up @@ -95,9 +95,13 @@ export const useIndexData = (
try {
const resp: IndexSearchResponse = await ml.esSearch(esSearchRequest);

const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields));
setRowCount(resp.hits.total.value);
setRowCountRelation(resp.hits.total.relation);
const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields ?? {}));
setRowCount(typeof resp.hits.total === 'number' ? resp.hits.total : resp.hits.total.value);
setRowCountRelation(
typeof resp.hits.total === 'number'
? ('eq' as estypes.TotalHitsRelation)
: resp.hits.total.relation
);
setTableItems(docs);
setStatus(INDEX_STATUS.LOADED);
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
isClassificationAnalysis,
isRegressionAnalysis,
} from '../../../../../../../common/util/analytics_utils';
import { HITS_TOTAL_RELATION } from '../../../../../../../common/types/es_client';
import { ES_CLIENT_TOTAL_HITS_RELATION } from '../../../../../../../common/types/es_client';

import { getToastNotifications } from '../../../../../util/dependency_cache';
import { useColorRange, ColorRangeLegend } from '../../../../../components/color_range_legend';
Expand Down Expand Up @@ -77,7 +77,7 @@ const getResultsSectionHeaderItems = (
defaultMessage="Total docs"
/>
),
value: `${rowCountRelation === HITS_TOTAL_RELATION.GTE ? '>' : ''}${rowCount}`,
value: `${rowCountRelation === ES_CLIENT_TOTAL_HITS_RELATION.GTE ? '>' : ''}${rowCount}`,
},
...(colorRange !== undefined
? [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ function findFieldsInJob(job: Job, datafeed: Datafeed) {
function findFieldsInAgg(obj: Record<string, object>) {
const fields: string[] = [];
Object.entries(obj).forEach(([key, val]) => {
if (isPopulatedObject(val)) {
if (
isPopulatedObject<Record<string, object>>(val) &&
Object.keys(val).every((d) => typeof d === 'object')
) {
fields.push(...findFieldsInAgg(val));
} else if (typeof val === 'string' && key === 'field') {
fields.push(val);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { MLJobEditor } from '../../../../../jobs_list/components/ml_job_editor';
import { mlJobService } from '../../../../../../services/job_service';
import { ML_DATA_PREVIEW_COUNT } from '../../../../../../../../common/util/job_utils';
import { isPopulatedObject } from '../../../../../../../../common/util/object_utils';
import { isMultiBucketAggregate } from '../../../../../../../../common/types/es_client';

export const DatafeedPreview: FC<{
combinedJob: CombinedJob | null;
Expand Down Expand Up @@ -67,7 +68,10 @@ export const DatafeedPreview: FC<{
// the first item under aggregations can be any name
if (isPopulatedObject(resp.aggregations)) {
const accessor = Object.keys(resp.aggregations)[0];
data = resp.aggregations[accessor].buckets.slice(0, ML_DATA_PREVIEW_COUNT);
const aggregate = resp.aggregations[accessor];
if (isMultiBucketAggregate(aggregate)) {
data = aggregate.buckets.slice(0, ML_DATA_PREVIEW_COUNT);
}
}

setPreviewJsonString(JSON.stringify(data, null, 2));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,7 @@ export interface SeriesConfigWithMetadata extends SeriesConfig {
}

export const isSeriesConfigWithMetadata = (arg: unknown): arg is SeriesConfigWithMetadata => {
return (
isPopulatedObject(arg) &&
{}.hasOwnProperty.call(arg, 'bucketSpanSeconds') &&
{}.hasOwnProperty.call(arg, 'detectorLabel')
);
return isPopulatedObject(arg, ['bucketSpanSeconds', 'detectorLabel']);
};

interface ChartRange {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* 2.0.
*/

import { SearchResponse } from 'elasticsearch';
import { estypes } from '@elastic/elasticsearch';

import { TimeRange } from 'src/plugins/data/common/query/timefilter/types';
import { CombinedJob, Datafeed, Job } from '../../../common/types/anomaly_detection_jobs';
import { Calendar } from '../../../common/types/calendars';
Expand Down Expand Up @@ -40,7 +41,7 @@ declare interface JobService {
): Promise<any>;
createResultsUrl(jobId: string[], start: number, end: number, location: string): string;
getJobAndGroupIds(): Promise<ExistingJobsAndGroups>;
searchPreview(job: CombinedJob): Promise<SearchResponse<any>>;
searchPreview(job: CombinedJob): Promise<estypes.SearchResponse<any>>;
getJob(jobId: string): CombinedJob;
loadJobsWrapper(): Promise<CombinedJob[]>;
}
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/ml/public/embeddables/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ export interface SwimLaneDrilldownContext extends EditSwimlanePanelContext {

export function isSwimLaneEmbeddable(arg: unknown): arg is SwimLaneDrilldownContext {
return (
isPopulatedObject(arg) &&
arg.hasOwnProperty('embeddable') &&
isPopulatedObject(arg, ['embeddable']) &&
isPopulatedObject(arg.embeddable, ['type']) &&
arg.embeddable.type === ANOMALY_SWIMLANE_EMBEDDABLE_TYPE
);
}
Expand Down Expand Up @@ -130,8 +130,8 @@ export function isAnomalyExplorerEmbeddable(
arg: unknown
): arg is AnomalyChartsFieldSelectionContext {
return (
isPopulatedObject(arg) &&
arg.hasOwnProperty('embeddable') &&
isPopulatedObject(arg, ['embeddable']) &&
isPopulatedObject(arg.embeddable, ['type']) &&
arg.embeddable.type === ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE
);
}
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export {
getSeverityType,
getFormattedSeverityScore,
} from '../common/util/anomaly_utils';
export { HITS_TOTAL_RELATION } from '../common/types/es_client';
export { ES_CLIENT_TOTAL_HITS_RELATION } from '../common/types/es_client';

export { ANOMALY_SEVERITY } from '../common';
export { useMlHref, ML_PAGES, MlUrlGenerator } from './ml_url_generator';
Expand Down
Loading

0 comments on commit cbba256

Please sign in to comment.