mirror of
https://github.com/grafana/grafana.git
synced 2025-09-20 01:39:53 +08:00
Transformations: Add time filtering to filter by value (#101591)
* WIP - time filtering * Replace variable test * Change tests * Validator is already tested * Change test to match evaluation * Add line to docs * Revert "Add line to docs" This reverts commit 783f247c33e854e00a72a42c5d04eee3aa923a5a. * Put transformations docs update in the right place, cannot build without an update, WIP * Run build * Use regex test and rewind * Does this help * make config optional
This commit is contained in:
@ -461,13 +461,13 @@ The available conditions for string fields are:
|
|||||||
- **Contains substring** - Match if the value contains the specified substring (case insensitive).
|
- **Contains substring** - Match if the value contains the specified substring (case insensitive).
|
||||||
- **Does not contain substring** - Match if the value doesn't contain the specified substring (case insensitive).
|
- **Does not contain substring** - Match if the value doesn't contain the specified substring (case insensitive).
|
||||||
|
|
||||||
The available conditions for number fields are:
|
The available conditions for number and time fields are:
|
||||||
|
|
||||||
- **Greater** - Match if the value is greater than the specified value.
|
- **Greater** - Match if the value is greater than the specified value.
|
||||||
- **Lower** - Match if the value is lower than the specified value.
|
- **Lower** - Match if the value is lower than the specified value.
|
||||||
- **Greater or equal** - Match if the value is greater or equal.
|
- **Greater or equal** - Match if the value is greater or equal.
|
||||||
- **Lower or equal** - Match if the value is lower or equal.
|
- **Lower or equal** - Match if the value is lower or equal.
|
||||||
- **Range** - Match a range between a specified minimum and maximum, min and max included.
|
- **Range** - Match a range between a specified minimum and maximum, min and max included. A time field will pre-populate with variables to filter by selected time.
|
||||||
|
|
||||||
Consider the following dataset:
|
Consider the following dataset:
|
||||||
|
|
||||||
|
@ -14,14 +14,25 @@ const isBetweenValueMatcher: ValueMatcherInfo<RangeValueMatcherOptions> = {
|
|||||||
if (isNaN(value)) {
|
if (isNaN(value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return value > options.from && value < options.to;
|
|
||||||
|
// if it is a time, it is interpolated as a string, so convert before comparing
|
||||||
|
const fromVal = typeof options.from !== 'number' ? parseInt(options.from, 10) : options.from;
|
||||||
|
const toVal = typeof options.to !== 'number' ? parseInt(options.to, 10) : options.to;
|
||||||
|
|
||||||
|
return value > fromVal && value < toVal;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getOptionsDisplayText: (options) => {
|
getOptionsDisplayText: (options) => {
|
||||||
return `Matches all rows where field value is between ${options.from} and ${options.to}.`;
|
return `Matches all rows where field value is between ${options.from} and ${options.to}.`;
|
||||||
},
|
},
|
||||||
isApplicable: (field) => field.type === FieldType.number,
|
isApplicable: (field) => field.type === FieldType.number || field.type === FieldType.time,
|
||||||
getDefaultOptions: () => ({ from: 0, to: 100 }),
|
getDefaultOptions: (field) => {
|
||||||
|
if (field.type === FieldType.time) {
|
||||||
|
return { from: '$__from', to: '$__to' };
|
||||||
|
} else {
|
||||||
|
return { from: 0, to: 100 };
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRangeValueMatchers = (): ValueMatcherInfo[] => [isBetweenValueMatcher];
|
export const getRangeValueMatchers = (): ValueMatcherInfo[] => [isBetweenValueMatcher];
|
||||||
|
@ -18,4 +18,4 @@ export const updateConfig = (update: Partial<GrafanaBootConfig>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// The `enable_alpha` flag is not exposed directly, this is equivalent
|
// The `enable_alpha` flag is not exposed directly, this is equivalent
|
||||||
export const hasAlphaPanels = Boolean(config.panels?.debug?.state === PluginState.alpha);
|
export const hasAlphaPanels = Boolean(config?.panels?.debug?.state === PluginState.alpha);
|
||||||
|
@ -70,7 +70,7 @@ export const expressionTypes: Array<SelectableValue<ExpressionQueryType>> = [
|
|||||||
},
|
},
|
||||||
].filter((expr) => {
|
].filter((expr) => {
|
||||||
if (expr.value === ExpressionQueryType.sql) {
|
if (expr.value === ExpressionQueryType.sql) {
|
||||||
return config.featureToggles?.sqlExpressions;
|
return config?.featureToggles?.sqlExpressions;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -380,13 +380,13 @@ The available conditions for string fields are:
|
|||||||
- **Contains substring** - Match if the value contains the specified substring (case insensitive).
|
- **Contains substring** - Match if the value contains the specified substring (case insensitive).
|
||||||
- **Does not contain substring** - Match if the value doesn't contain the specified substring (case insensitive).
|
- **Does not contain substring** - Match if the value doesn't contain the specified substring (case insensitive).
|
||||||
|
|
||||||
The available conditions for number fields are:
|
The available conditions for number and time fields are:
|
||||||
|
|
||||||
- **Greater** - Match if the value is greater than the specified value.
|
- **Greater** - Match if the value is greater than the specified value.
|
||||||
- **Lower** - Match if the value is lower than the specified value.
|
- **Lower** - Match if the value is lower than the specified value.
|
||||||
- **Greater or equal** - Match if the value is greater or equal.
|
- **Greater or equal** - Match if the value is greater or equal.
|
||||||
- **Lower or equal** - Match if the value is lower or equal.
|
- **Lower or equal** - Match if the value is lower or equal.
|
||||||
- **Range** - Match a range between a specified minimum and maximum, min and max included.
|
- **Range** - Match a range between a specified minimum and maximum, min and max included. A time field will pre-populate with variables to filter by selected time.
|
||||||
|
|
||||||
Consider the following dataset:
|
Consider the following dataset:
|
||||||
|
|
||||||
|
@ -27,19 +27,19 @@ describe('validator', () => {
|
|||||||
expect(numberOrVariableValidator('1')).toBe(true);
|
expect(numberOrVariableValidator('1')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('validats a string that is a negative integer', () => {
|
it('validates a string that is a negative integer', () => {
|
||||||
expect(numberOrVariableValidator('-1')).toBe(true);
|
expect(numberOrVariableValidator('-1')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('validats a string that is zero', () => {
|
it('validates a string that is zero', () => {
|
||||||
expect(numberOrVariableValidator('0')).toBe(true);
|
expect(numberOrVariableValidator('0')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('validats a string that is a float', () => {
|
it('validates a string that is a float', () => {
|
||||||
expect(numberOrVariableValidator('1.2')).toBe(true);
|
expect(numberOrVariableValidator('1.2')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('validats a string that is a negative float', () => {
|
it('validates a string that is a negative float', () => {
|
||||||
expect(numberOrVariableValidator('-1.2')).toBe(true);
|
expect(numberOrVariableValidator('-1.2')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ describe('validator', () => {
|
|||||||
expect(numberOrVariableValidator('$foo')).toBe(true);
|
expect(numberOrVariableValidator('$foo')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails a string that has multiple variables', () => {
|
it('validates a string that has multiple variables', () => {
|
||||||
expect(numberOrVariableValidator('$foo$asd')).toBe(false);
|
expect(numberOrVariableValidator('$foo$asd')).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ import {
|
|||||||
import { t } from '@grafana/i18n';
|
import { t } from '@grafana/i18n';
|
||||||
import { getTemplateSrv } from '@grafana/runtime';
|
import { getTemplateSrv } from '@grafana/runtime';
|
||||||
|
|
||||||
|
import { variableRegex } from '../variables/utils';
|
||||||
|
|
||||||
export const getAllFieldNamesFromDataFrames = (frames: DataFrame[], withBaseFieldNames = false) => {
|
export const getAllFieldNamesFromDataFrames = (frames: DataFrame[], withBaseFieldNames = false) => {
|
||||||
// get full names
|
// get full names
|
||||||
let names = frames.flatMap((frame) => frame.fields.map((field) => getFieldDisplayName(field, frame, frames)));
|
let names = frames.flatMap((frame) => frame.fields.map((field) => getFieldDisplayName(field, frame, frames)));
|
||||||
@ -77,7 +79,9 @@ export const numberOrVariableValidator = (value: string | number) => {
|
|||||||
if (!Number.isNaN(Number(value))) {
|
if (!Number.isNaN(Number(value))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (/^\$[A-Za-z0-9_]+$/.test(value)) {
|
const variableFound = variableRegex.test(value);
|
||||||
|
variableRegex.lastIndex = 0;
|
||||||
|
if (variableFound) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
Reference in New Issue
Block a user