mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Transforms: add sort by transformer (#30370)
This commit is contained in:
@@ -13,6 +13,7 @@ import { renameFieldsTransformer } from './transformers/rename';
|
||||
import { labelsToFieldsTransformer } from './transformers/labelsToFields';
|
||||
import { ensureColumnsTransformer } from './transformers/ensureColumns';
|
||||
import { groupByTransformer } from './transformers/groupBy';
|
||||
import { sortByTransformer } from './transformers/sortBy';
|
||||
import { mergeTransformer } from './transformers/merge';
|
||||
import { renameByRegexTransformer } from './transformers/renameByRegex';
|
||||
import { filterByValueTransformer } from './transformers/filterByValue';
|
||||
@@ -35,6 +36,7 @@ export const standardTransformers = {
|
||||
labelsToFieldsTransformer,
|
||||
ensureColumnsTransformer,
|
||||
groupByTransformer,
|
||||
sortByTransformer,
|
||||
mergeTransformer,
|
||||
renameByRegexTransformer,
|
||||
};
|
||||
|
||||
@@ -21,4 +21,5 @@ export enum DataTransformerID {
|
||||
noop = 'noop',
|
||||
ensureColumns = 'ensureColumns',
|
||||
groupBy = 'groupBy',
|
||||
sortBy = 'sortBy',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||
import { sortByTransformer, SortByTransformerOptions } from './sortBy';
|
||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||
import { transformDataFrame } from '../transformDataFrame';
|
||||
import { Field, FieldType } from '../../types';
|
||||
import { DataTransformerID } from './ids';
|
||||
import { DataTransformerConfig } from '@grafana/data';
|
||||
|
||||
const testFrame = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time, values: [10, 9, 8, 7, 6, 5] }, // desc
|
||||
{ name: 'text', type: FieldType.string, values: ['a', 'z', 'b', 'x', 'c'] },
|
||||
{ name: 'count', type: FieldType.string, values: [1, 2, 3, 4, 5] }, // asc
|
||||
],
|
||||
});
|
||||
|
||||
describe('SortBy transformer', () => {
|
||||
beforeAll(() => {
|
||||
mockTransformationsRegistry([sortByTransformer]);
|
||||
});
|
||||
|
||||
it('should not apply transformation if config is missing sort fields', async () => {
|
||||
const cfg: DataTransformerConfig<SortByTransformerOptions> = {
|
||||
id: DataTransformerID.sortBy,
|
||||
options: {
|
||||
sort: [], // nothing
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [testFrame])).toEmitValuesWith(received => {
|
||||
const result = received[0];
|
||||
expect(result[0]).toBe(testFrame);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort time asc', async () => {
|
||||
const cfg: DataTransformerConfig<SortByTransformerOptions> = {
|
||||
id: DataTransformerID.sortBy,
|
||||
options: {
|
||||
sort: [
|
||||
{
|
||||
field: 'time',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [testFrame])).toEmitValuesWith(received => {
|
||||
expect(getFieldSnapshot(received[0][0].fields[0])).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"name": "time",
|
||||
"values": Array [
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort time (desc)', async () => {
|
||||
const cfg: DataTransformerConfig<SortByTransformerOptions> = {
|
||||
id: DataTransformerID.sortBy,
|
||||
options: {
|
||||
sort: [
|
||||
{
|
||||
field: 'time',
|
||||
desc: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [testFrame])).toEmitValuesWith(received => {
|
||||
expect(getFieldSnapshot(received[0][0].fields[0])).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"name": "time",
|
||||
"values": Array [
|
||||
10,
|
||||
9,
|
||||
8,
|
||||
7,
|
||||
6,
|
||||
5,
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getFieldSnapshot(f: Field): Object {
|
||||
return { name: f.name, values: f.values.toArray() };
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
import { DataTransformerInfo } from '../../types/transformations';
|
||||
import { DataFrame } from '../../types';
|
||||
import { getFieldDisplayName } from '../../field';
|
||||
import { sortDataFrame } from '../../dataframe';
|
||||
|
||||
export interface SortByField {
|
||||
field: string;
|
||||
desc?: boolean;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
export interface SortByTransformerOptions {
|
||||
// NOTE: this structure supports an array, however only the first entry is used
|
||||
// future versions may support multi-sort options
|
||||
sort: SortByField[];
|
||||
}
|
||||
|
||||
export const sortByTransformer: DataTransformerInfo<SortByTransformerOptions> = {
|
||||
id: DataTransformerID.sortBy,
|
||||
name: 'Sort by',
|
||||
description: 'Sort fields in a frame',
|
||||
defaultOptions: {
|
||||
fields: {},
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a modified copy of the series. If the transform is not or should not
|
||||
* be applied, just return the input series
|
||||
*/
|
||||
operator: options => source =>
|
||||
source.pipe(
|
||||
map(data => {
|
||||
if (!Array.isArray(data) || data.length === 0 || !options?.sort?.length) {
|
||||
return data;
|
||||
}
|
||||
return sortDataFrames(data, options.sort);
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
export function sortDataFrames(data: DataFrame[], sort: SortByField[]): DataFrame[] {
|
||||
return data.map(frame => {
|
||||
const s = attachFieldIndex(frame, sort);
|
||||
if (s.length && s[0].index != null) {
|
||||
return sortDataFrame(frame, s[0].index, s[0].desc);
|
||||
}
|
||||
return frame;
|
||||
});
|
||||
}
|
||||
|
||||
function attachFieldIndex(frame: DataFrame, sort: SortByField[]): SortByField[] {
|
||||
return sort.map(s => {
|
||||
if (s.index != null) {
|
||||
// null or undefined
|
||||
return s;
|
||||
}
|
||||
return {
|
||||
...s,
|
||||
index: frame.fields.findIndex(f => s.field === getFieldDisplayName(f, frame)),
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user