mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
Transformations: Add substring matcher to the 'Filter by Value' transformation (#83548)
This commit is contained in:
parent
74115f1f08
commit
88ebef5cba
@ -21,6 +21,7 @@ import { getNullValueMatchers } from './matchers/valueMatchers/nullMatchers';
|
|||||||
import { getNumericValueMatchers } from './matchers/valueMatchers/numericMatchers';
|
import { getNumericValueMatchers } from './matchers/valueMatchers/numericMatchers';
|
||||||
import { getRangeValueMatchers } from './matchers/valueMatchers/rangeMatchers';
|
import { getRangeValueMatchers } from './matchers/valueMatchers/rangeMatchers';
|
||||||
import { getRegexValueMatcher } from './matchers/valueMatchers/regexMatchers';
|
import { getRegexValueMatcher } from './matchers/valueMatchers/regexMatchers';
|
||||||
|
import { getSubstringValueMatchers } from './matchers/valueMatchers/substringMatchers';
|
||||||
|
|
||||||
export { type FieldValueMatcherConfig } from './matchers/fieldValueMatcher';
|
export { type FieldValueMatcherConfig } from './matchers/fieldValueMatcher';
|
||||||
|
|
||||||
@ -59,6 +60,7 @@ export const valueMatchers = new Registry<ValueMatcherInfo>(() => {
|
|||||||
...getNullValueMatchers(),
|
...getNullValueMatchers(),
|
||||||
...getNumericValueMatchers(),
|
...getNumericValueMatchers(),
|
||||||
...getEqualValueMatchers(),
|
...getEqualValueMatchers(),
|
||||||
|
...getSubstringValueMatchers(),
|
||||||
...getRangeValueMatchers(),
|
...getRangeValueMatchers(),
|
||||||
...getRegexValueMatcher(),
|
...getRegexValueMatcher(),
|
||||||
];
|
];
|
||||||
|
@ -52,5 +52,7 @@ export enum ValueMatcherID {
|
|||||||
lowerOrEqual = 'lowerOrEqual',
|
lowerOrEqual = 'lowerOrEqual',
|
||||||
equal = 'equal',
|
equal = 'equal',
|
||||||
notEqual = 'notEqual',
|
notEqual = 'notEqual',
|
||||||
|
substring = 'substring',
|
||||||
|
notSubstring = 'notSubstring',
|
||||||
between = 'between',
|
between = 'between',
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
import { toDataFrame } from '../../../dataframe';
|
||||||
|
import { DataFrame } from '../../../types/dataFrame';
|
||||||
|
import { getValueMatcher } from '../../matchers';
|
||||||
|
import { ValueMatcherID } from '../ids';
|
||||||
|
|
||||||
|
describe('value substring to matcher', () => {
|
||||||
|
const data: DataFrame[] = [
|
||||||
|
toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'temp',
|
||||||
|
values: ['24', null, '10', 'asd', '42'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const matcher = getValueMatcher({
|
||||||
|
id: ValueMatcherID.substring,
|
||||||
|
options: {
|
||||||
|
value: '2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match when option value is a substring', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 0;
|
||||||
|
|
||||||
|
expect(matcher(valueIndex, field, frame, data)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Added for https://github.com/grafana/grafana/pull/83548#pullrequestreview-1904931540 where the matcher was not handling null values
|
||||||
|
it('should be a mismatch if the option is null and should not cause errors', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 1;
|
||||||
|
|
||||||
|
expect(matcher(valueIndex, field, frame, data)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not match when option value is different', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 2;
|
||||||
|
|
||||||
|
expect(matcher(valueIndex, field, frame, data)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match when option value is a substring', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 4;
|
||||||
|
|
||||||
|
expect(matcher(valueIndex, field, frame, data)).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('value not substring matcher', () => {
|
||||||
|
const data: DataFrame[] = [
|
||||||
|
toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'temp',
|
||||||
|
values: ['24', null, '050', 'asd', '42', '0'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const matcher = getValueMatcher({
|
||||||
|
id: ValueMatcherID.notSubstring,
|
||||||
|
options: {
|
||||||
|
value: '5',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not match if the value is "0" and the option value is "0"', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 5;
|
||||||
|
|
||||||
|
const zeroMatcher = getValueMatcher({
|
||||||
|
id: ValueMatcherID.notSubstring,
|
||||||
|
options: {
|
||||||
|
value: '0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(zeroMatcher(valueIndex, field, frame, data)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match when option value is a substring', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 0;
|
||||||
|
|
||||||
|
expect(matcher(valueIndex, field, frame, data)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not match when option value is different', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 2;
|
||||||
|
|
||||||
|
expect(matcher(valueIndex, field, frame, data)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match when value is null because null its not a substring', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 4;
|
||||||
|
|
||||||
|
expect(matcher(valueIndex, field, frame, data)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should not match if the option value is empty string', () => {
|
||||||
|
const frame = data[0];
|
||||||
|
const field = frame.fields[0];
|
||||||
|
const valueIndex = 0;
|
||||||
|
const emptyMatcher = getValueMatcher({
|
||||||
|
id: ValueMatcherID.notSubstring,
|
||||||
|
options: {
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(emptyMatcher(valueIndex, field, frame, data)).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,41 @@
|
|||||||
|
import { Field, FieldType } from '../../../types/dataFrame';
|
||||||
|
import { ValueMatcherInfo } from '../../../types/transformations';
|
||||||
|
import { ValueMatcherID } from '../ids';
|
||||||
|
|
||||||
|
import { BasicValueMatcherOptions } from './types';
|
||||||
|
|
||||||
|
const isSubstringMatcher: ValueMatcherInfo<BasicValueMatcherOptions> = {
|
||||||
|
id: ValueMatcherID.substring,
|
||||||
|
name: 'Contains Substring',
|
||||||
|
description: 'Match where value for given field is a substring to options value.',
|
||||||
|
get: (options) => {
|
||||||
|
return (valueIndex: number, field: Field) => {
|
||||||
|
const value = field.values[valueIndex];
|
||||||
|
return (value && value.includes(options.value)) || options.value === '';
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getOptionsDisplayText: () => {
|
||||||
|
return `Matches all rows where field is similar to the value.`;
|
||||||
|
},
|
||||||
|
isApplicable: (field) => field.type === FieldType.string,
|
||||||
|
getDefaultOptions: () => ({ value: '' }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const isNotSubstringValueMatcher: ValueMatcherInfo<BasicValueMatcherOptions> = {
|
||||||
|
id: ValueMatcherID.notSubstring,
|
||||||
|
name: 'Does not contain substring',
|
||||||
|
description: 'Match where value for given field is not a substring to options value.',
|
||||||
|
get: (options) => {
|
||||||
|
return (valueIndex: number, field: Field) => {
|
||||||
|
const value = field.values[valueIndex];
|
||||||
|
return typeof value === 'string' && options.value !== '' && !value.includes(options.value);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getOptionsDisplayText: () => {
|
||||||
|
return `Matches all rows where field is not similar to the value.`;
|
||||||
|
},
|
||||||
|
isApplicable: (field) => field.type === FieldType.string,
|
||||||
|
getDefaultOptions: () => ({ value: '' }),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSubstringValueMatchers = (): ValueMatcherInfo[] => [isSubstringMatcher, isNotSubstringValueMatcher];
|
@ -127,5 +127,19 @@ export const getBasicValueMatchersUI = (): Array<ValueMatcherUIRegistryItem<Basi
|
|||||||
validator: () => true,
|
validator: () => true,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Is Substring',
|
||||||
|
id: ValueMatcherID.substring,
|
||||||
|
component: basicMatcherEditor<string | number | boolean>({
|
||||||
|
validator: () => true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Is not substring',
|
||||||
|
id: ValueMatcherID.notSubstring,
|
||||||
|
component: basicMatcherEditor<string | number | boolean>({
|
||||||
|
validator: () => true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user