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

[Lens] Add percentile function #86490

Merged
merged 11 commits into from
Dec 23, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -1329,8 +1329,10 @@ describe('IndexPatternDimensionEditorPanel', () => {
'Median',
'Minimum',
'Moving average',
'Percentile',
'Sum',
'Unique count',
'\u00a0',
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,53 @@ describe('IndexPattern Data Source', () => {
expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp', 'another_datefield']);
});

it('should add the suffix to the remap column id if provided by the operation', async () => {
const queryBaseState: IndexPatternBaseState = {
currentIndexPatternId: '1',
layers: {
first: {
indexPatternId: '1',
columnOrder: ['def', 'abc'],
columns: {
abc: {
label: '23rd percentile',
dataType: 'number',
isBucketed: false,
sourceField: 'bytes',
operationType: 'percentile',
params: {
percentile: 23,
},
},
def: {
label: 'Terms',
dataType: 'string',
isBucketed: true,
operationType: 'terms',
sourceField: 'source',
params: {
size: 5,
orderBy: {
type: 'alphabetical',
},
orderDirection: 'asc',
},
},
},
},
},
};

const state = enrichBaseState(queryBaseState);

const ast = indexPatternDatasource.toExpression(state, 'first') as Ast;
expect(Object.keys(JSON.parse(ast.chain[1].arguments.idMap[0] as string))).toEqual([
'col-0-def',
// col-1 is the auto naming of esasggs, abc is the specified column id, .23 is the generated suffix
'col-1-abc.23',
]);
});

it('should add time_scale and format function if time scale is set and supported', async () => {
const queryBaseState: IndexPatternBaseState = {
currentIndexPatternId: '1',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
hasDateField,
} from './utils';
import { updateColumnParam } from '../../layer_helpers';
import { useDebounceWithOptions } from '../helpers';
import { isValidNumber, useDebounceWithOptions } from '../helpers';
import { adjustTimeScaleOnOtherColumnChange } from '../../time_scale_utils';
import type { OperationDefinition, ParamEditorProps } from '..';

Expand Down Expand Up @@ -122,19 +122,6 @@ export const movingAverageOperation: OperationDefinition<
timeScalingMode: 'optional',
};

function isValidNumber(input: string) {
if (input === '') return false;
try {
const val = parseFloat(input);
if (isNaN(val)) return false;
if (val < 1) return false;
if (val.toString().includes('.')) return false;
} catch (e) {
return false;
}
return true;
}

function MovingAverageParamEditor({
layer,
updateLayer,
Expand All @@ -145,7 +132,7 @@ function MovingAverageParamEditor({

useDebounceWithOptions(
() => {
if (!isValidNumber(inputValue)) return;
if (!isValidNumber(inputValue, true, undefined, 1)) return;
const inputNumber = parseInt(inputValue, 10);
updateLayer(
updateColumnParam({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ describe('date_histogram', () => {
const esAggsFn = dateHistogramOperation.toEsAggsFn(
layer.columns.col1 as DateHistogramIndexPatternColumn,
'col1',
indexPattern1
indexPattern1,
layer
);
expect(esAggsFn).toEqual(
expect.objectContaining({
Expand Down Expand Up @@ -250,7 +251,8 @@ describe('date_histogram', () => {
},
},
]),
}
},
layer
);
expect(esAggsFn).toEqual(
expect.objectContaining({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ describe('filters', () => {
const esAggsFn = filtersOperation.toEsAggsFn(
layer.columns.col1 as FiltersIndexPatternColumn,
'col1',
createMockedIndexPattern()
createMockedIndexPattern(),
layer
);
expect(esAggsFn).toEqual(
expect.objectContaining({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { useRef } from 'react';
import useDebounce from 'react-use/lib/useDebounce';
import { i18n } from '@kbn/i18n';
import { operationDefinitionMap } from '.';
import { IndexPatternColumn, operationDefinitionMap } from '.';
import { FieldBasedIndexPatternColumn } from './column_types';
import { IndexPattern } from '../../types';

Expand Down Expand Up @@ -63,6 +63,13 @@ export function getInvalidFieldMessage(
: undefined;
}

export function getEsAggsSuffix(column: IndexPatternColumn) {
const operationDefinition = operationDefinitionMap[column.operationType];
return operationDefinition.input === 'field' && operationDefinition.getEsAggsSuffix
? operationDefinition.getEsAggsSuffix(column)
: '';
}

export function getSafeName(name: string, indexPattern: IndexPattern): string {
const field = indexPattern.getFieldByName(name);
return field
Expand All @@ -71,3 +78,24 @@ export function getSafeName(name: string, indexPattern: IndexPattern): string {
defaultMessage: 'Missing field',
});
}

export function isValidNumber(
inputValue: string | number | null | undefined,
integer?: boolean,
upperBound?: number,
lowerBound?: number
) {
const inputValueAsNumber = Number(inputValue);
// an input is value if it's not an empty string, parses to a valid number, is between 0 and 100 (inclusive)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is checked for 0 to 100 inclusiveness?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment was outdated, removed it

// and is an integer
return (
inputValue !== '' &&
inputValue !== null &&
inputValue !== undefined &&
!Number.isNaN(inputValueAsNumber) &&
Number.isFinite(inputValueAsNumber) &&
(!integer || Number.isInteger(inputValueAsNumber)) &&
(upperBound === undefined || inputValueAsNumber <= upperBound) &&
(lowerBound === undefined || inputValueAsNumber >= lowerBound)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { termsOperation, TermsIndexPatternColumn } from './terms';
import { filtersOperation, FiltersIndexPatternColumn } from './filters';
import { cardinalityOperation, CardinalityIndexPatternColumn } from './cardinality';
import { percentileOperation, PercentileIndexPatternColumn } from './percentile';
import {
minOperation,
MinIndexPatternColumn,
Expand Down Expand Up @@ -58,6 +59,7 @@ export type IndexPatternColumn =
| CardinalityIndexPatternColumn
| SumIndexPatternColumn
| MedianIndexPatternColumn
| PercentileIndexPatternColumn
| CountIndexPatternColumn
| LastValueIndexPatternColumn
| CumulativeSumIndexPatternColumn
Expand All @@ -82,6 +84,7 @@ const internalOperationDefinitions = [
cardinalityOperation,
sumOperation,
medianOperation,
percentileOperation,
lastValueOperation,
countOperation,
rangeOperation,
Expand All @@ -96,6 +99,7 @@ export { rangeOperation } from './ranges';
export { filtersOperation } from './filters';
export { dateHistogramOperation } from './date_histogram';
export { minOperation, averageOperation, sumOperation, maxOperation } from './metrics';
export { percentileOperation } from './percentile';
export { countOperation } from './count';
export { lastValueOperation } from './last_value';
export {
Expand Down Expand Up @@ -223,7 +227,12 @@ interface FieldlessOperationDefinition<C extends BaseIndexPatternColumn> {
* Function turning a column into an agg config passed to the `esaggs` function
* together with the agg configs returned from other columns.
*/
toEsAggsFn: (column: C, columnId: string, indexPattern: IndexPattern) => ExpressionAstFunction;
toEsAggsFn: (
column: C,
columnId: string,
indexPattern: IndexPattern,
layer: IndexPatternLayer
) => ExpressionAstFunction;
}

interface FieldBasedOperationDefinition<C extends BaseIndexPatternColumn> {
Expand Down Expand Up @@ -262,7 +271,19 @@ interface FieldBasedOperationDefinition<C extends BaseIndexPatternColumn> {
* Function turning a column into an agg config passed to the `esaggs` function
* together with the agg configs returned from other columns.
*/
toEsAggsFn: (column: C, columnId: string, indexPattern: IndexPattern) => ExpressionAstFunction;
toEsAggsFn: (
column: C,
columnId: string,
indexPattern: IndexPattern,
layer: IndexPatternLayer
) => ExpressionAstFunction;
/**
* Optional function to return the suffix used for ES bucket paths and esaggs column id.
* This is relevant for multi metrics to pick the right value.
*
* @param column The current column
*/
getEsAggsSuffix?: (column: C) => string;
/**
* Validate that the operation has the right preconditions in the state. For example:
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ describe('last_value', () => {
const esAggsFn = lastValueOperation.toEsAggsFn(
{ ...lastValueColumn, params: { ...lastValueColumn.params } },
'col1',
{} as IndexPattern
{} as IndexPattern,
layer
);
expect(esAggsFn).toEqual(
expect.objectContaining({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,24 @@ function buildMetricOperation<T extends MetricColumn<string>>({
},
onOtherColumnChanged: (layer, thisColumnId, changedColumnId) =>
optionalTimeScaling
? adjustTimeScaleOnOtherColumnChange(layer, thisColumnId, changedColumnId)
: layer.columns[thisColumnId],
? (adjustTimeScaleOnOtherColumnChange(layer, thisColumnId, changedColumnId) as T)
: (layer.columns[thisColumnId] as T),
getDefaultLabel: (column, indexPattern, columns) =>
labelLookup(getSafeName(column.sourceField, indexPattern), column),
buildColumn: ({ field, previousColumn }) => ({
label: labelLookup(field.displayName, previousColumn),
dataType: 'number',
operationType: type,
sourceField: field.name,
isBucketed: false,
scale: 'ratio',
timeScale: optionalTimeScaling ? previousColumn?.timeScale : undefined,
params:
previousColumn && previousColumn.dataType === 'number' ? previousColumn.params : undefined,
}),
buildColumn: ({ field, previousColumn }) =>
({
label: labelLookup(field.displayName, previousColumn),
dataType: 'number',
operationType: type,
sourceField: field.name,
isBucketed: false,
scale: 'ratio',
timeScale: optionalTimeScaling ? previousColumn?.timeScale : undefined,
params:
previousColumn && previousColumn.dataType === 'number'
? previousColumn.params
: undefined,
} as T),
onFieldChange: (oldColumn, field) => {
return {
...oldColumn,
Expand Down
Loading