mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Transform: Add a limit transform (#49291)
* Add a limit transform * Add a limit transform * Simplify filter code Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
parent
062d255124
commit
7d7890e23c
2
.yarn/sdks/integrations.yml
vendored
2
.yarn/sdks/integrations.yml
vendored
@ -2,5 +2,5 @@
|
||||
# Manual changes might be lost!
|
||||
|
||||
integrations:
|
||||
- vscode
|
||||
- vim
|
||||
- vscode
|
||||
|
@ -545,3 +545,26 @@ Here is the result after applying the Series to rows transformation.
|
||||
## Sort by
|
||||
|
||||
This transformation will sort each frame by the configured field, When `reverse` is checked, the values will return in the opposite order.
|
||||
|
||||
## Limit
|
||||
|
||||
Use this transformation to limit the number of rows displayed.
|
||||
|
||||
In the example below, we have the following response from the data source:
|
||||
|
||||
| Time | Metric | Value |
|
||||
| ------------------- | ----------- | ----- |
|
||||
| 2020-07-07 11:34:20 | Temperature | 25 |
|
||||
| 2020-07-07 11:34:20 | Humidity | 22 |
|
||||
| 2020-07-07 10:32:20 | Humidity | 29 |
|
||||
| 2020-07-07 10:31:22 | Temperature | 22 |
|
||||
| 2020-07-07 09:30:57 | Humidity | 33 |
|
||||
| 2020-07-07 09:30:05 | Temperature | 19 |
|
||||
|
||||
Here is the result after adding a Limit transformation with a value of '3':
|
||||
|
||||
| Time | Metric | Value |
|
||||
| ------------------- | ----------- | ----- |
|
||||
| 2020-07-07 11:34:20 | Temperature | 25 |
|
||||
| 2020-07-07 11:34:20 | Humidity | 22 |
|
||||
| 2020-07-07 10:32:20 | Humidity | 29 |
|
||||
|
@ -10,6 +10,7 @@ import { groupByTransformer } from './transformers/groupBy';
|
||||
import { groupingToMatrixTransformer } from './transformers/groupingToMatrix';
|
||||
import { histogramTransformer } from './transformers/histogram';
|
||||
import { labelsToFieldsTransformer } from './transformers/labelsToFields';
|
||||
import { limitTransformer } from './transformers/limit';
|
||||
import { mergeTransformer } from './transformers/merge';
|
||||
import { noopTransformer } from './transformers/noop';
|
||||
import { orderFieldsTransformer } from './transformers/order';
|
||||
@ -45,4 +46,5 @@ export const standardTransformers = {
|
||||
histogramTransformer,
|
||||
convertFieldTypeTransformer,
|
||||
groupingToMatrixTransformer,
|
||||
limitTransformer,
|
||||
};
|
||||
|
@ -33,4 +33,5 @@ export enum DataTransformerID {
|
||||
joinByLabels = 'joinByLabels',
|
||||
extractFields = 'extractFields',
|
||||
groupingToMatrix = 'groupingToMatrix',
|
||||
limit = 'limit',
|
||||
}
|
||||
|
@ -0,0 +1,104 @@
|
||||
import { DataTransformerConfig } from '@grafana/data';
|
||||
|
||||
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||
import { Field, FieldType } from '../../types';
|
||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||
import { ArrayVector } from '../../vector';
|
||||
import { transformDataFrame } from '../transformDataFrame';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
import { limitTransformer, LimitTransformerOptions } from './limit';
|
||||
|
||||
describe('Limit transformer', () => {
|
||||
beforeAll(() => {
|
||||
mockTransformationsRegistry([limitTransformer]);
|
||||
});
|
||||
|
||||
it('should limit the number of items', async () => {
|
||||
const testSeries = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000, 7000, 8000] },
|
||||
{ name: 'message', type: FieldType.string, values: ['one', 'two', 'two', 'three', 'three', 'three'] },
|
||||
{ name: 'values', type: FieldType.number, values: [1, 2, 2, 3, 3, 3] },
|
||||
],
|
||||
});
|
||||
|
||||
const cfg: DataTransformerConfig<LimitTransformerOptions> = {
|
||||
id: DataTransformerID.limit,
|
||||
options: {
|
||||
limitField: 3,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [testSeries])).toEmitValuesWith((received) => {
|
||||
const result = received[0];
|
||||
const expected: Field[] = [
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: new ArrayVector([3000, 4000, 5000]),
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: FieldType.string,
|
||||
values: new ArrayVector(['one', 'two', 'two']),
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'values',
|
||||
type: FieldType.number,
|
||||
values: new ArrayVector([1, 2, 2]),
|
||||
config: {},
|
||||
},
|
||||
];
|
||||
|
||||
expect(result[0].fields).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not limit the number of items if limit > number of items', async () => {
|
||||
const testSeries = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000, 7000, 8000] },
|
||||
{ name: 'message', type: FieldType.string, values: ['one', 'two', 'two', 'three', 'three', 'three'] },
|
||||
{ name: 'values', type: FieldType.number, values: [1, 2, 2, 3, 3, 3] },
|
||||
],
|
||||
});
|
||||
|
||||
const cfg: DataTransformerConfig<LimitTransformerOptions> = {
|
||||
id: DataTransformerID.limit,
|
||||
options: {
|
||||
limitField: 7,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [testSeries])).toEmitValuesWith((received) => {
|
||||
const result = received[0];
|
||||
const expected: Field[] = [
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: new ArrayVector([3000, 4000, 5000, 6000, 7000, 8000]),
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: FieldType.string,
|
||||
values: new ArrayVector(['one', 'two', 'two', 'three', 'three', 'three']),
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: 'values',
|
||||
type: FieldType.number,
|
||||
values: new ArrayVector([1, 2, 2, 3, 3, 3]),
|
||||
config: {},
|
||||
},
|
||||
];
|
||||
|
||||
expect(result[0].fields).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { DataTransformerInfo } from '../../types';
|
||||
import { ArrayVector } from '../../vector/ArrayVector';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
|
||||
export interface LimitTransformerOptions {
|
||||
limitField?: number;
|
||||
}
|
||||
|
||||
const DEFAULT_LIMIT_FIELD = 10;
|
||||
|
||||
export const limitTransformer: DataTransformerInfo<LimitTransformerOptions> = {
|
||||
id: DataTransformerID.limit,
|
||||
name: 'Limit',
|
||||
description: 'Limit the number of items to the top N',
|
||||
defaultOptions: {
|
||||
limitField: DEFAULT_LIMIT_FIELD,
|
||||
},
|
||||
|
||||
operator: (options) => (source) =>
|
||||
source.pipe(
|
||||
map((data) => {
|
||||
const limitFieldMatch = options.limitField || DEFAULT_LIMIT_FIELD;
|
||||
return data.map((frame) => {
|
||||
if (frame.length > limitFieldMatch) {
|
||||
return {
|
||||
...frame,
|
||||
fields: frame.fields.map((f) => {
|
||||
const vals = f.values.toArray();
|
||||
return {
|
||||
...f,
|
||||
values: new ArrayVector(vals.slice(0, limitFieldMatch)),
|
||||
};
|
||||
}),
|
||||
length: limitFieldMatch,
|
||||
};
|
||||
}
|
||||
|
||||
return frame;
|
||||
});
|
||||
})
|
||||
),
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
import React, { FormEvent, useCallback } from 'react';
|
||||
|
||||
import { DataTransformerID, standardTransformers, TransformerRegistryItem, TransformerUIProps } from '@grafana/data';
|
||||
import { LimitTransformerOptions } from '@grafana/data/src/transformations/transformers/limit';
|
||||
import { InlineField, InlineFieldRow, Input } from '@grafana/ui';
|
||||
|
||||
export const LimitTransformerEditor: React.FC<TransformerUIProps<LimitTransformerOptions>> = ({
|
||||
options,
|
||||
onChange,
|
||||
}) => {
|
||||
const onSetLimit = useCallback(
|
||||
(value: FormEvent<HTMLInputElement>) => {
|
||||
onChange({
|
||||
...options,
|
||||
limitField: Number(value.currentTarget.value),
|
||||
});
|
||||
},
|
||||
[onChange, options]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Limit" labelWidth={8}>
|
||||
<Input
|
||||
placeholder="Limit count"
|
||||
pattern="[0-9]*"
|
||||
value={options.limitField}
|
||||
onChange={onSetLimit}
|
||||
width={25}
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const limitTransformRegistryItem: TransformerRegistryItem<LimitTransformerOptions> = {
|
||||
id: DataTransformerID.limit,
|
||||
editor: LimitTransformerEditor,
|
||||
transformation: standardTransformers.limitTransformer,
|
||||
name: 'Limit',
|
||||
description: `Limit the number of items displayed.`,
|
||||
};
|
@ -12,6 +12,7 @@ import { groupByTransformRegistryItem } from './editors/GroupByTransformerEditor
|
||||
import { groupingToMatrixTransformRegistryItem } from './editors/GroupingToMatrixTransformerEditor';
|
||||
import { histogramTransformRegistryItem } from './editors/HistogramTransformerEditor';
|
||||
import { labelsToFieldsTransformerRegistryItem } from './editors/LabelsToFieldsTransformerEditor';
|
||||
import { limitTransformRegistryItem } from './editors/LimitTransformerEditor';
|
||||
import { mergeTransformerRegistryItem } from './editors/MergeTransformerEditor';
|
||||
import { organizeFieldsTransformRegistryItem } from './editors/OrganizeFieldsTransformerEditor';
|
||||
import { reduceTransformRegistryItem } from './editors/ReduceTransformerEditor';
|
||||
@ -52,6 +53,7 @@ export const getStandardTransformers = (): Array<TransformerRegistryItem<any>> =
|
||||
extractFieldsTransformRegistryItem,
|
||||
heatmapTransformRegistryItem,
|
||||
groupingToMatrixTransformRegistryItem,
|
||||
limitTransformRegistryItem,
|
||||
joinByLabelsTransformRegistryItem,
|
||||
];
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user