Transformer: labels to fields should not also merge frames (#38671)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||
import { LabelsToFieldsOptions, labelsToFieldsTransformer } from './labelsToFields';
|
||||
import { DataTransformerConfig, FieldDTO, FieldType } from '../../types';
|
||||
import { DataFrame, DataTransformerConfig, FieldDTO, FieldType } from '../../types';
|
||||
import { DataTransformerID } from './ids';
|
||||
import { toDataFrame, toDataFrameDTO } from '../../dataframe';
|
||||
import { transformDataFrame } from '../transformDataFrame';
|
||||
@@ -26,21 +26,26 @@ describe('Labels as Columns', () => {
|
||||
|
||||
await expect(transformDataFrame([cfg], [source])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const result = toDataFrameDTO(data[0]);
|
||||
|
||||
const expected: FieldDTO[] = [
|
||||
{ name: 'time', type: FieldType.time, values: [1000, 2000], config: {} },
|
||||
{
|
||||
name: 'location',
|
||||
type: FieldType.string,
|
||||
values: ['inside', 'inside'],
|
||||
config: {},
|
||||
},
|
||||
{ name: 'feelsLike', type: FieldType.string, values: ['ok', 'ok'], config: {} },
|
||||
{ name: 'Value', type: FieldType.number, values: [1, 2], config: {} },
|
||||
];
|
||||
|
||||
expect(result.fields).toEqual(expected);
|
||||
expect(toSimpleObject(data[0])).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"Value": Array [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
"feelsLike": Array [
|
||||
"ok",
|
||||
"ok",
|
||||
],
|
||||
"location": Array [
|
||||
"inside",
|
||||
"inside",
|
||||
],
|
||||
"time": Array [
|
||||
1000,
|
||||
2000,
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,13 +78,13 @@ describe('Labels as Columns', () => {
|
||||
|
||||
const expected: FieldDTO[] = [
|
||||
{ name: 'time', type: FieldType.time, values: [1000, 2000], config: {} },
|
||||
{ name: 'Request', type: FieldType.number, values: [1, 2], config: {} },
|
||||
{
|
||||
name: 'location',
|
||||
type: FieldType.string,
|
||||
values: ['inside', 'inside'],
|
||||
config: {},
|
||||
},
|
||||
{ name: 'Request', type: FieldType.number, values: [1, 2], config: {} },
|
||||
];
|
||||
|
||||
expect(result.fields).toEqual(expected);
|
||||
@@ -110,15 +115,42 @@ describe('Labels as Columns', () => {
|
||||
|
||||
await expect(transformDataFrame([cfg], [oneValueOneLabelA, oneValueOneLabelB])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const result = toDataFrameDTO(data[0]);
|
||||
expect(data.length).toEqual(2);
|
||||
|
||||
const expected: FieldDTO[] = [
|
||||
{ name: 'time', type: FieldType.time, values: [1000, 2000], config: {} },
|
||||
{ name: 'location', type: FieldType.string, values: ['inside', 'outside'], config: {} },
|
||||
{ name: 'temp', type: FieldType.number, values: [1, -1], config: {} },
|
||||
];
|
||||
|
||||
expect(result.fields).toEqual(expected);
|
||||
expect(toSimpleObject(data[0])).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"location": Array [
|
||||
"inside",
|
||||
],
|
||||
"temp": Array [
|
||||
1,
|
||||
],
|
||||
"time": Array [
|
||||
1000,
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(toSimpleObject(data[1])).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"location": Array [
|
||||
"outside",
|
||||
],
|
||||
"temp": Array [
|
||||
-1,
|
||||
],
|
||||
"time": Array [
|
||||
2000,
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function toSimpleObject(frame: DataFrame) {
|
||||
const obj: any = {};
|
||||
for (const field of frame.fields) {
|
||||
obj[field.name] = field.values.toArray();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { DataFrame, DataTransformerInfo, Field, FieldType } from '../../types';
|
||||
import { DataFrame, Field, FieldType, SynchronousDataTransformerInfo } from '../../types';
|
||||
import { DataTransformerID } from './ids';
|
||||
import { ArrayVector } from '../../vector';
|
||||
import { mergeTransformer } from './merge';
|
||||
|
||||
export interface LabelsToFieldsOptions {
|
||||
/*
|
||||
@@ -12,65 +11,61 @@ export interface LabelsToFieldsOptions {
|
||||
valueLabel?: string;
|
||||
}
|
||||
|
||||
export const labelsToFieldsTransformer: DataTransformerInfo<LabelsToFieldsOptions> = {
|
||||
export const labelsToFieldsTransformer: SynchronousDataTransformerInfo<LabelsToFieldsOptions> = {
|
||||
id: DataTransformerID.labelsToFields,
|
||||
name: 'Labels to fields',
|
||||
description: 'Extract time series labels to fields (columns)',
|
||||
defaultOptions: {},
|
||||
operator: (options) => (source) =>
|
||||
source.pipe(
|
||||
map((data) => {
|
||||
const result: DataFrame[] = [];
|
||||
|
||||
for (const frame of data) {
|
||||
const newFields: Field[] = [];
|
||||
operator: (options) => (source) => source.pipe(map((data) => labelsToFieldsTransformer.transformer(options)(data))),
|
||||
|
||||
for (const field of frame.fields) {
|
||||
if (!field.labels) {
|
||||
newFields.push(field);
|
||||
continue;
|
||||
}
|
||||
transformer: (options: LabelsToFieldsOptions) => (data: DataFrame[]) => {
|
||||
const result: DataFrame[] = [];
|
||||
|
||||
let name = field.name;
|
||||
for (const frame of data) {
|
||||
const newFields: Field[] = [];
|
||||
|
||||
for (const labelName of Object.keys(field.labels)) {
|
||||
// if we should use this label as the value field name store it and skip adding this as a separate field
|
||||
if (options.valueLabel === labelName) {
|
||||
name = field.labels[labelName];
|
||||
continue;
|
||||
}
|
||||
|
||||
const values = new Array(frame.length).fill(field.labels[labelName]);
|
||||
newFields.push({
|
||||
name: labelName,
|
||||
type: FieldType.string,
|
||||
values: new ArrayVector(values),
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
|
||||
// add the value field but clear out any labels or displayName
|
||||
newFields.push({
|
||||
...field,
|
||||
name,
|
||||
config: {
|
||||
...field.config,
|
||||
// we need to clear thes for this transform as these can contain label names that we no longer want
|
||||
displayName: undefined,
|
||||
displayNameFromDS: undefined,
|
||||
},
|
||||
labels: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
result.push({
|
||||
fields: newFields,
|
||||
length: frame.length,
|
||||
});
|
||||
for (const field of frame.fields) {
|
||||
if (!field.labels) {
|
||||
newFields.push(field);
|
||||
continue;
|
||||
}
|
||||
|
||||
return result;
|
||||
}),
|
||||
mergeTransformer.operator({})
|
||||
),
|
||||
const sansLabels = {
|
||||
...field,
|
||||
config: {
|
||||
...field.config,
|
||||
// we need to clear thes for this transform as these can contain label names that we no longer want
|
||||
displayName: undefined,
|
||||
displayNameFromDS: undefined,
|
||||
},
|
||||
labels: undefined,
|
||||
};
|
||||
newFields.push(sansLabels);
|
||||
|
||||
for (const labelName of Object.keys(field.labels)) {
|
||||
// if we should use this label as the value field name store it and skip adding this as a separate field
|
||||
if (options.valueLabel === labelName) {
|
||||
sansLabels.name = field.labels[labelName];
|
||||
continue;
|
||||
}
|
||||
|
||||
const values = new Array(frame.length).fill(field.labels[labelName]);
|
||||
newFields.push({
|
||||
name: labelName,
|
||||
type: FieldType.string,
|
||||
values: new ArrayVector(values),
|
||||
config: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
result.push({
|
||||
fields: newFields,
|
||||
length: frame.length,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user