Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Transforms: Filter aggregation support #67591

Merged
merged 28 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e903125
[ML] WIP filter support
darnautov May 26, 2020
72a4a9c
[ML] value selector
darnautov May 27, 2020
eef2b25
[ML] only supported filter aggs as options
darnautov May 27, 2020
e23e374
[ML] WIP apply config
darnautov May 29, 2020
4e4cffe
[ML] fix form persistence
darnautov May 29, 2020
36928c6
[ML] refactor
darnautov May 29, 2020
1e69ee7
[ML] support clone
darnautov May 30, 2020
3ddf9ad
[ML] validation, get es config
darnautov May 31, 2020
2496938
[ML] support "exists", fixes for the term form, validation
darnautov Jun 1, 2020
f4ef699
[ML] fix ts issues
darnautov Jun 1, 2020
2e89683
[ML] don't perform request on adding incomplete agg
darnautov Jun 1, 2020
d758648
[ML] basic range number support
darnautov Jun 1, 2020
9a2f734
[ML] filter bool agg support
darnautov Jun 1, 2020
d74e5a2
[ML] functional tests
darnautov Jun 2, 2020
da31710
[ML] getAggConfigFromEsAgg tests
darnautov Jun 2, 2020
6d1e707
[ML] fix unit tests
darnautov Jun 2, 2020
608cda5
[ML] agg name update on config change, add unit tests
darnautov Jun 2, 2020
48b1964
[ML] update snapshot
darnautov Jun 2, 2020
a8664ee
[ML] range selector enhancements
darnautov Jun 3, 2020
8745791
[ML] improve types
darnautov Jun 3, 2020
c353ad1
[ML] update step for range selector to support float numbers
darnautov Jun 3, 2020
69ee8ae
[ML] range validation
darnautov Jun 3, 2020
66308fa
[ML] term selector improvements
darnautov Jun 3, 2020
57b75c4
[ML] fix switch between advanced editor
darnautov Jun 3, 2020
67a85e1
Merge branch 'master' into ML-60015-transforms-filter-agg
elasticmachine Jun 3, 2020
a9723ed
[ML] prefix test ids
darnautov Jun 4, 2020
e2ce842
Merge remote-tracking branch 'origin/ML-60015-transforms-filter-agg' …
darnautov Jun 4, 2020
7cf1833
[ML] support helper text for aggs item
darnautov Jun 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions x-pack/plugins/ml/common/util/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export function requiredValidator() {
};
}

export type ValidationResult = object | null;

export function memoryInputValidator(allowedUnits = ALLOWED_DATA_UNITS) {
return (value: any) => {
if (typeof value !== 'string' || value === '') {
Expand Down
47 changes: 47 additions & 0 deletions x-pack/plugins/transform/public/app/common/pivot_aggs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { getAggConfigFromEsAgg } from './pivot_aggs';
import {
FilterAggForm,
FilterTermForm,
} from '../sections/create_transform/components/step_define/common/filter_agg/components';

describe('getAggConfigFromEsAgg', () => {
test('should throw an error for unsupported agg', () => {
expect(() => getAggConfigFromEsAgg({ terms: {} }, 'test')).toThrowError();
});

test('should return a common config if the agg does not have a custom config defined', () => {
expect(getAggConfigFromEsAgg({ avg: { field: 'region' } }, 'test_1')).toEqual({
agg: 'avg',
aggName: 'test_1',
dropDownName: 'test_1',
field: 'region',
});
});

test('should return a custom config for recognized aggregation type', () => {
expect(
getAggConfigFromEsAgg({ filter: { term: { region: 'sa-west-1' } } }, 'test_2')
).toMatchObject({
agg: 'filter',
aggName: 'test_2',
dropDownName: 'test_2',
field: 'region',
AggFormComponent: FilterAggForm,
aggConfig: {
filterAgg: 'term',
aggTypeConfig: {
FilterAggFormComponent: FilterTermForm,
filterAggConfig: {
value: 'sa-west-1',
},
},
},
});
});
});
158 changes: 130 additions & 28 deletions x-pack/plugins/transform/public/app/common/pivot_aggs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,51 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { FC } from 'react';
import { Dictionary } from '../../../common/types/common';
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common';

import { AggName } from './aggregations';
import { EsFieldName } from './fields';
import { getAggFormConfig } from '../sections/create_transform/components/step_define/common/get_agg_form_config';
import { PivotAggsConfigFilter } from '../sections/create_transform/components/step_define/common/filter_agg/types';

export enum PIVOT_SUPPORTED_AGGS {
AVG = 'avg',
CARDINALITY = 'cardinality',
MAX = 'max',
MIN = 'min',
PERCENTILES = 'percentiles',
SUM = 'sum',
VALUE_COUNT = 'value_count',
export type PivotSupportedAggs = typeof PIVOT_SUPPORTED_AGGS[keyof typeof PIVOT_SUPPORTED_AGGS];

export function isPivotSupportedAggs(arg: any): arg is PivotSupportedAggs {
return Object.values(PIVOT_SUPPORTED_AGGS).includes(arg);
}

export const PIVOT_SUPPORTED_AGGS = {
AVG: 'avg',
CARDINALITY: 'cardinality',
MAX: 'max',
MIN: 'min',
PERCENTILES: 'percentiles',
SUM: 'sum',
VALUE_COUNT: 'value_count',
FILTER: 'filter',
} as const;

export const PERCENTILES_AGG_DEFAULT_PERCENTS = [1, 5, 25, 50, 75, 95, 99];

export const pivotAggsFieldSupport = {
[KBN_FIELD_TYPES.ATTACHMENT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.BOOLEAN]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.ATTACHMENT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
[KBN_FIELD_TYPES.BOOLEAN]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
[KBN_FIELD_TYPES.DATE]: [
PIVOT_SUPPORTED_AGGS.MAX,
PIVOT_SUPPORTED_AGGS.MIN,
PIVOT_SUPPORTED_AGGS.VALUE_COUNT,
PIVOT_SUPPORTED_AGGS.FILTER,
],
[KBN_FIELD_TYPES.GEO_POINT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
[KBN_FIELD_TYPES.GEO_SHAPE]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
[KBN_FIELD_TYPES.IP]: [
PIVOT_SUPPORTED_AGGS.CARDINALITY,
PIVOT_SUPPORTED_AGGS.VALUE_COUNT,
PIVOT_SUPPORTED_AGGS.FILTER,
],
[KBN_FIELD_TYPES.GEO_POINT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.GEO_SHAPE]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.IP]: [PIVOT_SUPPORTED_AGGS.CARDINALITY, PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.MURMUR3]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.MURMUR3]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
[KBN_FIELD_TYPES.NUMBER]: [
PIVOT_SUPPORTED_AGGS.AVG,
PIVOT_SUPPORTED_AGGS.CARDINALITY,
Expand All @@ -42,49 +57,122 @@ export const pivotAggsFieldSupport = {
PIVOT_SUPPORTED_AGGS.PERCENTILES,
PIVOT_SUPPORTED_AGGS.SUM,
PIVOT_SUPPORTED_AGGS.VALUE_COUNT,
PIVOT_SUPPORTED_AGGS.FILTER,
],
[KBN_FIELD_TYPES.STRING]: [
PIVOT_SUPPORTED_AGGS.CARDINALITY,
PIVOT_SUPPORTED_AGGS.VALUE_COUNT,
PIVOT_SUPPORTED_AGGS.FILTER,
],
[KBN_FIELD_TYPES.STRING]: [PIVOT_SUPPORTED_AGGS.CARDINALITY, PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES._SOURCE]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.UNKNOWN]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES.CONFLICT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT],
[KBN_FIELD_TYPES._SOURCE]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
[KBN_FIELD_TYPES.UNKNOWN]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
[KBN_FIELD_TYPES.CONFLICT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
};

export type PivotAgg = {
[key in PIVOT_SUPPORTED_AGGS]?: {
[key in PivotSupportedAggs]?: {
field: EsFieldName;
};
};

export type PivotAggDict = { [key in AggName]: PivotAgg };
export type PivotAggDict = {
[key in AggName]: PivotAgg;
};

// The internal representation of an aggregation definition.
export interface PivotAggsConfigBase {
agg: PIVOT_SUPPORTED_AGGS;
agg: PivotSupportedAggs;
aggName: AggName;
dropDownName: string;
}

interface PivotAggsConfigWithUiBase extends PivotAggsConfigBase {
/**
* Resolves agg UI config from provided ES agg definition
*/
export function getAggConfigFromEsAgg(esAggDefinition: Record<string, any>, aggName: string) {
const aggKeys = Object.keys(esAggDefinition);

// Find the main aggregation key
const agg = aggKeys.find((aggKey) => aggKey !== 'aggs');

if (!isPivotSupportedAggs(agg)) {
throw new Error(`Aggregation "${agg}" is not supported`);
}

const commonConfig: PivotAggsConfigBase = {
...esAggDefinition[agg],
agg,
aggName,
dropDownName: aggName,
};

const config = getAggFormConfig(agg, commonConfig);

if (isPivotAggsWithExtendedForm(config)) {
config.setUiConfigFromEs(esAggDefinition[agg]);
}

if (aggKeys.includes('aggs')) {
// TODO process sub-aggregation
}

return config;
}

export interface PivotAggsConfigWithUiBase extends PivotAggsConfigBase {
field: EsFieldName;
}

export interface PivotAggsConfigWithExtra<T> extends PivotAggsConfigWithUiBase {
/** Form component */
AggFormComponent: FC<{
aggConfig: Partial<T>;
onChange: (arg: Partial<T>) => void;
selectedField: string;
}>;
/** Aggregation specific configuration */
aggConfig: Partial<T>;
/** Set UI configuration from ES aggregation definition */
setUiConfigFromEs: (arg: { [key: string]: any }) => void;
/** Converts UI agg config form to ES agg request object */
getEsAggConfig: () => { [key: string]: any } | null;
/** Indicates if the configuration is valid */
isValid: () => boolean;
/** Provides aggregation name generated based on the configuration */
getAggName?: () => string | undefined;
/** Helper text for the aggregation reflecting some configuration info */
helperText?: () => string | undefined;
}

interface PivotAggsConfigPercentiles extends PivotAggsConfigWithUiBase {
agg: PIVOT_SUPPORTED_AGGS.PERCENTILES;
agg: typeof PIVOT_SUPPORTED_AGGS.PERCENTILES;
percents: number[];
}

export type PivotAggsConfigWithUiSupport = PivotAggsConfigWithUiBase | PivotAggsConfigPercentiles;
export type PivotAggsConfigWithUiSupport =
| PivotAggsConfigWithUiBase
| PivotAggsConfigPercentiles
| PivotAggsConfigWithExtendedForm;

export function isPivotAggsConfigWithUiSupport(arg: any): arg is PivotAggsConfigWithUiSupport {
return (
arg.hasOwnProperty('agg') &&
arg.hasOwnProperty('aggName') &&
arg.hasOwnProperty('dropDownName') &&
arg.hasOwnProperty('field') &&
Object.values(PIVOT_SUPPORTED_AGGS).includes(arg.agg)
isPivotSupportedAggs(arg.agg)
);
}

/**
* Union type for agg configs with extended forms
*/
type PivotAggsConfigWithExtendedForm = PivotAggsConfigFilter;

export function isPivotAggsWithExtendedForm(arg: any): arg is PivotAggsConfigWithExtendedForm {
return arg.hasOwnProperty('AggFormComponent');
}

export function isPivotAggsConfigPercentiles(arg: any): arg is PivotAggsConfigPercentiles {
return (
arg.hasOwnProperty('agg') &&
Expand All @@ -99,14 +187,28 @@ export type PivotAggsConfig = PivotAggsConfigBase | PivotAggsConfigWithUiSupport
export type PivotAggsConfigWithUiSupportDict = Dictionary<PivotAggsConfigWithUiSupport>;
export type PivotAggsConfigDict = Dictionary<PivotAggsConfig>;

export function getEsAggFromAggConfig(groupByConfig: PivotAggsConfigBase): PivotAgg {
const esAgg = { ...groupByConfig };
/**
* Extracts Elasticsearch-ready aggregation configuration
* from the UI config
*/
export function getEsAggFromAggConfig(
pivotAggsConfig: PivotAggsConfigBase | PivotAggsConfigWithExtendedForm
): PivotAgg | null {
let esAgg: { [key: string]: any } | null = { ...pivotAggsConfig };

delete esAgg.agg;
delete esAgg.aggName;
delete esAgg.dropDownName;

if (isPivotAggsWithExtendedForm(pivotAggsConfig)) {
esAgg = pivotAggsConfig.getEsAggConfig();

if (esAgg === null) {
return null;
}
}

return {
[groupByConfig.agg]: esAgg,
[pivotAggsConfig.agg]: esAgg,
};
}
6 changes: 5 additions & 1 deletion x-pack/plugins/transform/public/app/common/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ export function getPreviewRequestBody(
});

aggs.forEach((agg) => {
request.pivot.aggregations[agg.aggName] = getEsAggFromAggConfig(agg);
const result = getEsAggFromAggConfig(agg);
if (result === null) {
return;
}
request.pivot.aggregations[agg.aggName] = result;
});

return request;
Expand Down
Loading