mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 10:03:33 -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 { getRangeValueMatchers } from './matchers/valueMatchers/rangeMatchers';
|
||||
import { getRegexValueMatcher } from './matchers/valueMatchers/regexMatchers';
|
||||
import { getSubstringValueMatchers } from './matchers/valueMatchers/substringMatchers';
|
||||
|
||||
export { type FieldValueMatcherConfig } from './matchers/fieldValueMatcher';
|
||||
|
||||
@ -59,6 +60,7 @@ export const valueMatchers = new Registry<ValueMatcherInfo>(() => {
|
||||
...getNullValueMatchers(),
|
||||
...getNumericValueMatchers(),
|
||||
...getEqualValueMatchers(),
|
||||
...getSubstringValueMatchers(),
|
||||
...getRangeValueMatchers(),
|
||||
...getRegexValueMatcher(),
|
||||
];
|
||||
|
@ -52,5 +52,7 @@ export enum ValueMatcherID {
|
||||
lowerOrEqual = 'lowerOrEqual',
|
||||
equal = 'equal',
|
||||
notEqual = 'notEqual',
|
||||
substring = 'substring',
|
||||
notSubstring = 'notSubstring',
|
||||
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,
|
||||
}),
|
||||
},
|
||||
{
|
||||
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