Transformations: Use fieldMatchers for convertFieldType and add doc (#38769)

This commit is contained in:
nikki-kiga 2021-09-02 19:18:06 -07:00 committed by GitHub
parent caef39b6a4
commit 2a2f10da7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 30 deletions

View File

@ -10,6 +10,7 @@ Grafana comes with the following transformations:
- [Add field from calculation]({{< relref "./types-options.md#add-field-from-calculation" >}})
- [Concatenate fields]({{< relref "./types-options.md#concatenate-fields" >}})
- [Config from query results]({{< relref "./config-from-query.md" >}})
- [Convert field type]({{< relref "./types-options.md#convert-field-type" >}})
- [Filter data by name]({{< relref "./types-options.md#filter-data-by-name" >}})
- [Filter data by query]({{< relref "./types-options.md#filter-data-by-query" >}})
- [Filter data by value]({{< relref "./types-options.md#filter-data-by-value" >}})
@ -324,6 +325,36 @@ After you concatenate the fields, the data frame would be:
| ---- | ------- | --- | ------ |
| 15.4 | 1230233 | 3.2 | 5 |
## Convert field type
This transformation changes the field type of the specified field.
- **Field -** Select from available fields
- **as -** Select the FieldType to convert to
- **Numeric -** attempts to make the values numbers
- **String -** will make the values strings
- **Time -** attempts to parse the values as time
- Will show an option to specify a DateFormat as input by a string like yyyy-mm-dd or DD MM YYYY hh:mm:ss
- **Boolean -** will make the values booleans
For example the following query could be modified by selecting the time field, as Time, and Date Format as YYYY.
| Time | Mark | Value |
| ---------- | ----- | ----- |
| 2017-07-01 | above | 25 |
| 2018-08-02 | below | 22 |
| 2019-09-02 | below | 29 |
| 2020-10-04 | above | 22 |
The result:
| Time | Mark | Value |
| ------------------- | ----- | ----- |
| 2017-01-01 00:00:00 | above | 25 |
| 2018-01-01 00:00:00 | below | 22 |
| 2019-01-01 00:00:00 | below | 29 |
| 2020-01-01 00:00:00 | above | 22 |
## Series to rows
> **Note:** This transformation is available in Grafana 7.1+.

View File

@ -5,20 +5,28 @@ import { DataTransformerID } from './ids';
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
import { dateTimeParse } from '../../datetime';
import { ArrayVector } from '../../vector';
import { fieldMatchers } from '../matchers';
import { FieldMatcherID } from '../matchers/ids';
export interface ConvertFieldTypeTransformerOptions {
conversions: ConvertFieldTypeOptions[];
}
export interface ConvertFieldTypeOptions {
/**
* The field to convert field type
*/
targetField?: string;
/**
* The field type to convert to
*/
destinationType?: FieldType;
/**
* Date format to parse a string datetime
*/
dateFormat?: string;
}
/**
* @alpha
*/
export const convertFieldTypeTransformer: SynchronousDataTransformerInfo<ConvertFieldTypeTransformerOptions> = {
id: DataTransformerID.convertFieldType,
name: 'Convert field type',
@ -43,32 +51,44 @@ export const convertFieldTypeTransformer: SynchronousDataTransformerInfo<Convert
};
/**
* @alpha
* Convert field types for dataframe(s)
* @param options - field type conversion options
* @param frames - dataframe(s) with field types to convert
* @returns dataframe(s) with converted field types
*/
export function convertFieldTypes(options: ConvertFieldTypeTransformerOptions, frames: DataFrame[]): DataFrame[] {
if (!options.conversions.length) {
return frames;
}
const frameCopy: DataFrame[] = [];
const framesCopy = frames.map((frame) => ({ ...frame }));
frames.forEach((frame) => {
for (let fieldIdx = 0; fieldIdx < frame.fields.length; fieldIdx++) {
let field = frame.fields[fieldIdx];
for (let cIdx = 0; cIdx < options.conversions.length; cIdx++) {
if (field.name === options.conversions[cIdx].targetField) {
//check in about matchers with Ryan
const conversion = options.conversions[cIdx];
frame.fields[fieldIdx] = convertFieldType(field, conversion);
break;
}
}
for (const conversion of options.conversions) {
if (!conversion.targetField) {
continue;
}
frameCopy.push(frame);
});
return frameCopy;
const matches = fieldMatchers.get(FieldMatcherID.byName).get(conversion.targetField);
for (const frame of framesCopy) {
frame.fields = frame.fields.map((field) => {
if (matches(field, frame, framesCopy)) {
return convertFieldType(field, conversion);
}
return field;
});
}
}
return framesCopy;
}
/**
* Convert a single field type to specifed field type.
* @param field - field to convert
* @param opts - field conversion options
* @returns converted field
*
* @internal
*/
export function convertFieldType(field: Field, opts: ConvertFieldTypeOptions): Field {
switch (opts.destinationType) {
case FieldType.time:
@ -84,6 +104,9 @@ export function convertFieldType(field: Field, opts: ConvertFieldTypeOptions): F
}
}
/**
* @internal
*/
export function fieldToTimeField(field: Field, dateFormat?: string): Field {
let opts = dateFormat ? { format: dateFormat } : undefined;
@ -109,12 +132,8 @@ function fieldToNumberField(field: Field): Field {
const numValues = field.values.toArray().slice();
for (let n = 0; n < numValues.length; n++) {
if (numValues[n]) {
let number = +numValues[n];
numValues[n] = Number.isFinite(number) ? number : null;
} else {
numValues[n] = null;
}
const number = +numValues[n];
numValues[n] = Number.isFinite(number) ? number : null;
}
return {
@ -128,7 +147,7 @@ function fieldToBooleanField(field: Field): Field {
const booleanValues = field.values.toArray().slice();
for (let b = 0; b < booleanValues.length; b++) {
booleanValues[b] = Boolean(booleanValues[b]);
booleanValues[b] = Boolean(!!booleanValues[b]);
}
return {
@ -153,7 +172,12 @@ function fieldToStringField(field: Field): Field {
}
/**
* @alpha
* Checks the first value. Assumes any number should be time fieldtype. Otherwise attempts to make the fieldtype time.
* @param field - field to ensure is a time fieldtype
* @param dateFormat - date format used to parse a string datetime
* @returns field as time
*
* @public
*/
export function ensureTimeField(field: Field, dateFormat?: string): Field {
const firstValueTypeIsNumber = typeof field.values.get(0) === 'number';

View File

@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { ChangeEvent, useCallback } from 'react';
import {
DataTransformerID,
FieldNamePickerConfigSettings,
@ -56,9 +56,9 @@ export const ConvertFieldTypeTransformerEditor: React.FC<TransformerUIProps<Conv
);
const onInputFormat = useCallback(
(idx) => (value: SelectableValue<string>) => {
(idx) => (e: ChangeEvent<HTMLInputElement>) => {
const conversions = options.conversions;
conversions[idx] = { ...conversions[idx], dateFormat: value.value };
conversions[idx] = { ...conversions[idx], dateFormat: e.currentTarget.value };
onChange({
...options,
conversions: conversions,