ExtractFields: properly detect value types from extracted fields (#41995) (#42001)

(cherry picked from commit 82cf49143d)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
Grot (@grafanabot) 2021-11-19 13:10:23 -05:00 committed by GitHub
parent 3d4a5ce8fa
commit a9da6b8175
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 35 deletions

View File

@ -11,6 +11,7 @@ import { DataFrameDTO, FieldType, TableData, TimeSeries } from '../types/index';
import { dateTime } from '../datetime/moment_wrapper'; import { dateTime } from '../datetime/moment_wrapper';
import { MutableDataFrame } from './MutableDataFrame'; import { MutableDataFrame } from './MutableDataFrame';
import { ArrayDataFrame } from './ArrayDataFrame'; import { ArrayDataFrame } from './ArrayDataFrame';
import { getFieldTypeFromValue } from '.';
describe('toDataFrame', () => { describe('toDataFrame', () => {
it('converts timeseries to series', () => { it('converts timeseries to series', () => {
@ -118,15 +119,13 @@ describe('toDataFrame', () => {
expect(guessFieldTypeFromValue('xxxx')).toBe(FieldType.string); expect(guessFieldTypeFromValue('xxxx')).toBe(FieldType.string);
}); });
it('Guess Column Types from strings', () => { it('Get column types from values', () => {
expect(guessFieldTypeFromValue('1')).toBe(FieldType.number); expect(getFieldTypeFromValue(1)).toBe(FieldType.number);
expect(guessFieldTypeFromValue('1.234')).toBe(FieldType.number); expect(getFieldTypeFromValue(1.234)).toBe(FieldType.number);
expect(guessFieldTypeFromValue('NaN')).toBe(FieldType.number); expect(getFieldTypeFromValue(NaN)).toBe(FieldType.number);
expect(guessFieldTypeFromValue('3.125e7')).toBe(FieldType.number); expect(getFieldTypeFromValue(3.125e7)).toBe(FieldType.number);
expect(guessFieldTypeFromValue('True')).toBe(FieldType.boolean); expect(getFieldTypeFromValue(true)).toBe(FieldType.boolean);
expect(guessFieldTypeFromValue('FALSE')).toBe(FieldType.boolean); expect(getFieldTypeFromValue('xxxx')).toBe(FieldType.string);
expect(guessFieldTypeFromValue('true')).toBe(FieldType.boolean);
expect(guessFieldTypeFromValue('xxxx')).toBe(FieldType.string);
}); });
it('Guess Column Types from series', () => { it('Guess Column Types from series', () => {

View File

@ -189,10 +189,33 @@ export function guessFieldTypeFromNameAndValue(name: string, v: any): FieldType
return guessFieldTypeFromValue(v); return guessFieldTypeFromValue(v);
} }
/**
* Check the field type to see what the contents are
*/
export function getFieldTypeFromValue(v: any): FieldType {
if (v instanceof Date || isDateTime(v)) {
return FieldType.time;
}
if (isNumber(v)) {
return FieldType.number;
}
if (isString(v)) {
return FieldType.string;
}
if (isBoolean(v)) {
return FieldType.boolean;
}
return FieldType.other;
}
/** /**
* Given a value this will guess the best column type * Given a value this will guess the best column type
* *
* TODO: better Date/Time support! Look for standard date strings? * NOTE: this is will try to see if string values can be mapped to other types (like number)
*/ */
export function guessFieldTypeFromValue(v: any): FieldType { export function guessFieldTypeFromValue(v: any): FieldType {
if (v instanceof Date || isDateTime(v)) { if (v instanceof Date || isDateTime(v)) {
@ -237,7 +260,7 @@ export function guessFieldTypeForField(field: Field): FieldType | undefined {
// 2. Check the first non-null value // 2. Check the first non-null value
for (let i = 0; i < field.values.length; i++) { for (let i = 0; i < field.values.length; i++) {
const v = field.values.get(i); const v = field.values.get(i);
if (v !== null) { if (v != null) {
return guessFieldTypeFromValue(v); return guessFieldTypeFromValue(v);
} }
} }

View File

@ -14,23 +14,28 @@ describe('Fields from JSON', () => {
const frames = extractFieldsTransformer.transformer(cfg)([data]); const frames = extractFieldsTransformer.transformer(cfg)([data]);
expect(frames.length).toEqual(1); expect(frames.length).toEqual(1);
expect(frames[0].fields.map((v) => v.name)).toMatchInlineSnapshot(` expect(
Array [ frames[0].fields.reduce((acc, v) => {
"a", acc[v.name] = v.type;
"av", return acc;
"c", }, {} as any)
"e", ).toMatchInlineSnapshot(`
"ev", Object {
"h", "a": "string",
"l", "av": "number",
"o", "c": "string",
"op", "e": "number",
"s", "ev": "string",
"sym", "h": "string",
"v", "l": "string",
"vw", "o": "string",
"z", "op": "string",
] "s": "number",
"sym": "string",
"v": "number",
"vw": "string",
"z": "number",
}
`); `);
}); });
}); });

View File

@ -4,7 +4,7 @@ import {
DataTransformerID, DataTransformerID,
Field, Field,
FieldType, FieldType,
guessFieldTypeForField, getFieldTypeFromValue,
SynchronousDataTransformerInfo, SynchronousDataTransformerInfo,
} from '@grafana/data'; } from '@grafana/data';
import { findField } from 'app/features/dimensions'; import { findField } from 'app/features/dimensions';
@ -72,14 +72,13 @@ function addExtractedFields(frame: DataFrame, options: ExtractFieldsOptions): Da
} }
const fields = names.map((name) => { const fields = names.map((name) => {
const f: Field = { const buffer = values.get(name);
return {
name, name,
values: new ArrayVector(values.get(name)), values: new ArrayVector(buffer),
type: FieldType.boolean, type: buffer ? getFieldTypeFromValue(buffer.find((v) => v != null)) : FieldType.other,
config: {}, config: {},
}; } as Field;
f.type = guessFieldTypeForField(f) ?? FieldType.other;
return f;
}); });
if (!options.replace) { if (!options.replace) {