mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
FieldValues: Implement array accessors for deprecated Vector types (#66807)
This commit is contained in:
@@ -3,11 +3,8 @@ import { map } from 'rxjs/operators';
|
||||
|
||||
import { getTimeField } from '../../dataframe/processDataFrame';
|
||||
import { getFieldDisplayName } from '../../field';
|
||||
import { DataFrame, DataTransformerInfo, Field, FieldType, NullValueMode, Vector } from '../../types';
|
||||
import { DataFrame, DataTransformerInfo, Field, FieldType, NullValueMode } from '../../types';
|
||||
import { BinaryOperationID, binaryOperators } from '../../utils/binaryOperators';
|
||||
import { BinaryOperationVector, ConstantVector } from '../../vector';
|
||||
import { AsNumberVector } from '../../vector/AsNumberVector';
|
||||
import { RowVector } from '../../vector/RowVector';
|
||||
import { doStandardCalcs, fieldReducers, ReducerID } from '../fieldReducer';
|
||||
import { getFieldMatcher } from '../matchers';
|
||||
import { FieldMatcherID } from '../matchers/ids';
|
||||
@@ -61,7 +58,7 @@ export interface CalculateFieldTransformerOptions {
|
||||
// TODO: config?: FieldConfig; or maybe field overrides? since the UI exists
|
||||
}
|
||||
|
||||
type ValuesCreator = (data: DataFrame) => Vector;
|
||||
type ValuesCreator = (data: DataFrame) => any[];
|
||||
|
||||
export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransformerOptions> = {
|
||||
id: DataTransformerID.calculateField,
|
||||
@@ -181,7 +178,7 @@ function getReduceRowCreator(options: ReduceOptions, allFrames: DataFrame[]): Va
|
||||
|
||||
return (frame: DataFrame) => {
|
||||
// Find the columns that should be examined
|
||||
const columns: Vector[] = [];
|
||||
const columns: any[] = [];
|
||||
for (const field of frame.fields) {
|
||||
if (matcher(field, frame, allFrames)) {
|
||||
columns.push(field.values);
|
||||
@@ -189,26 +186,31 @@ function getReduceRowCreator(options: ReduceOptions, allFrames: DataFrame[]): Va
|
||||
}
|
||||
|
||||
// Prepare a "fake" field for the row
|
||||
const iter = new RowVector(columns);
|
||||
const size = columns.length;
|
||||
const row: Field = {
|
||||
name: 'temp',
|
||||
values: iter,
|
||||
values: new Array(size),
|
||||
type: FieldType.number,
|
||||
config: {},
|
||||
};
|
||||
const vals: number[] = [];
|
||||
|
||||
for (let i = 0; i < frame.length; i++) {
|
||||
iter.rowIndex = i;
|
||||
const val = reducer(row, ignoreNulls, nullAsZero)[options.reducer];
|
||||
vals.push(val);
|
||||
for (let j = 0; j < size; j++) {
|
||||
row.values[j] = columns[j][i];
|
||||
}
|
||||
vals.push(reducer(row, ignoreNulls, nullAsZero)[options.reducer]);
|
||||
}
|
||||
|
||||
return vals;
|
||||
};
|
||||
}
|
||||
|
||||
function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFrames: DataFrame[]): Vector | undefined {
|
||||
function findFieldValuesWithNameOrConstant(
|
||||
frame: DataFrame,
|
||||
name: string,
|
||||
allFrames: DataFrame[]
|
||||
): number[] | undefined {
|
||||
if (!name) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -216,7 +218,7 @@ function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFr
|
||||
for (const f of frame.fields) {
|
||||
if (name === getFieldDisplayName(f, frame, allFrames)) {
|
||||
if (f.type === FieldType.boolean) {
|
||||
return new AsNumberVector(f.values);
|
||||
return f.values.map((v) => (v ? 1 : 0));
|
||||
}
|
||||
return f.values;
|
||||
}
|
||||
@@ -224,7 +226,7 @@ function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFr
|
||||
|
||||
const v = parseFloat(name);
|
||||
if (!isNaN(v)) {
|
||||
return new ConstantVector(v, frame.length);
|
||||
return new Array(frame.length).fill(v);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -237,10 +239,14 @@ function getBinaryCreator(options: BinaryOptions, allFrames: DataFrame[]): Value
|
||||
const left = findFieldValuesWithNameOrConstant(frame, options.left, allFrames);
|
||||
const right = findFieldValuesWithNameOrConstant(frame, options.right, allFrames);
|
||||
if (!left || !right || !operator) {
|
||||
return undefined as unknown as Vector;
|
||||
return undefined as unknown as any[];
|
||||
}
|
||||
|
||||
return new BinaryOperationVector(left, right, operator.operation);
|
||||
const arr = new Array(left.length);
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i] = operator.operation(left[i], right[i]);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ function fieldToNumberField(field: Field): Field {
|
||||
for (let n = 0; n < numValues.length; n++) {
|
||||
let toBeConverted = numValues[n];
|
||||
|
||||
if (valuesAsStrings) {
|
||||
if (valuesAsStrings && toBeConverted != null) {
|
||||
// some numbers returned from datasources have commas
|
||||
// strip the commas, coerce the string to a number
|
||||
toBeConverted = toBeConverted.replace(/,/g, '');
|
||||
|
||||
@@ -97,3 +97,30 @@ export interface ReadWriteVector<T = any> extends Vector<T> {}
|
||||
* @deprecated -- this is now part of the base Vector interface
|
||||
*/
|
||||
export interface MutableVector<T = any> extends ReadWriteVector<T> {}
|
||||
|
||||
/**
|
||||
* This is an extremely inefficient Vector wrapper that allows vectors to
|
||||
* be treated as arrays. We should avoid using this wrapper, but it is helpful
|
||||
* for a clean migration to arrays
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export function makeArrayIndexableVector<T extends Vector>(v: T): T {
|
||||
return new Proxy(v, {
|
||||
get(target: Vector, property: string, receiver: Vector) {
|
||||
const idx = +property;
|
||||
if (String(idx) === property) {
|
||||
return target.get(idx);
|
||||
}
|
||||
return Reflect.get(target, property, receiver);
|
||||
},
|
||||
set(target: Vector, property: string, value: any, receiver: Vector) {
|
||||
const idx = +property;
|
||||
if (String(idx) === property) {
|
||||
target.set(idx, value);
|
||||
return true;
|
||||
}
|
||||
return Reflect.set(target, property, value, receiver);
|
||||
},
|
||||
}) as T;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ describe('Check Appending Vector', () => {
|
||||
appended.append(new ArrayVector([4, 5, 6]));
|
||||
appended.append(new ArrayVector([7, 8, 9]));
|
||||
expect(appended.length).toEqual(9);
|
||||
expect(appended[0]).toEqual(1);
|
||||
expect(appended[1]).toEqual(2);
|
||||
expect(appended[100]).toEqual(undefined);
|
||||
|
||||
appended.setLength(5);
|
||||
expect(appended.length).toEqual(5);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Vector } from '../types/vector';
|
||||
import { Vector, makeArrayIndexableVector } from '../types/vector';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
@@ -14,7 +14,7 @@ interface AppendedVectorInfo<T> {
|
||||
* RAM -- rather than allocate a new array the size of all previous arrays, this just
|
||||
* points the correct index to their original array values
|
||||
*
|
||||
* @deprecated use a simple Arrays
|
||||
* @deprecated use a simple Arrays. NOTE this is not used in grafana core
|
||||
*/
|
||||
export class AppendedVectors<T = any> extends FunctionalVector<T> {
|
||||
length = 0;
|
||||
@@ -23,6 +23,7 @@ export class AppendedVectors<T = any> extends FunctionalVector<T> {
|
||||
constructor(startAt = 0) {
|
||||
super();
|
||||
this.length = startAt;
|
||||
return makeArrayIndexableVector(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
import { Vector } from '../types';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
|
||||
/**
|
||||
* This will force all values to be numbers
|
||||
*
|
||||
* @public
|
||||
* @deprecated use a simple Arrays
|
||||
* @deprecated use a simple Arrays. NOTE: Not used in grafana core
|
||||
*/
|
||||
export class AsNumberVector extends FunctionalVector<number> {
|
||||
constructor(private field: Vector) {
|
||||
export class AsNumberVector extends Array<number> {
|
||||
constructor(field: Vector) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.field.length;
|
||||
}
|
||||
|
||||
get(index: number) {
|
||||
return +this.field.get(index);
|
||||
return field.map((v) => +v) as AsNumberVector;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,13 @@ describe('ScaledVector', () => {
|
||||
const operation = binaryOperators.get(BinaryOperationID.Multiply).operation;
|
||||
const v = new BinaryOperationVector(source, new ConstantVector(scale, source.length), operation);
|
||||
expect(v.length).toEqual(source.length);
|
||||
// expect(v.push(10)).toEqual(source.length); // not implemented
|
||||
for (let i = 0; i < 10; i++) {
|
||||
// Accessed with getters
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(v.get(i)).toEqual(source.get(i) * scale);
|
||||
}
|
||||
// Accessed with array index
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(v[i]).toEqual(source[i] * scale);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,31 +1,18 @@
|
||||
import { Vector } from '../types/vector';
|
||||
import { BinaryOperation } from '../utils/binaryOperators';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use a simple Arrays
|
||||
* @deprecated use a simple Arrays. NOTE: Not used in grafana core
|
||||
*/
|
||||
export class BinaryOperationVector extends FunctionalVector<number> {
|
||||
constructor(private left: Vector<number>, private right: Vector<number>, private operation: BinaryOperation) {
|
||||
export class BinaryOperationVector extends Array<number> {
|
||||
constructor(left: Vector<number>, right: Vector<number>, operation: BinaryOperation) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.left.length;
|
||||
}
|
||||
|
||||
get(index: number): number {
|
||||
return this.operation(this.left.get(index), this.right.get(index));
|
||||
}
|
||||
|
||||
toArray(): number[] {
|
||||
return vectorToArray(this);
|
||||
}
|
||||
|
||||
toJSON(): number[] {
|
||||
return vectorToArray(this);
|
||||
const arr = new Array(left.length);
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i] = operation(left[i], right[i]);
|
||||
}
|
||||
return arr as BinaryOperationVector;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ describe('Check Circular Vector', () => {
|
||||
const buffer = [1, 2, 3];
|
||||
const v = new CircularVector({ buffer }); // tail is default option
|
||||
expect(v.toArray()).toEqual([1, 2, 3]);
|
||||
expect(v[0]).toEqual(1);
|
||||
expect(v[1]).toEqual(2);
|
||||
expect(v[2]).toEqual(3);
|
||||
expect(v[3]).toEqual(1); // loops back to one
|
||||
|
||||
v.add(4);
|
||||
expect(v.toArray()).toEqual([2, 3, 4]);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { makeArrayIndexableVector } from '../types';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
|
||||
interface CircularOptions<T> {
|
||||
@@ -34,6 +36,7 @@ export class CircularVector<T = any> extends FunctionalVector<T> {
|
||||
if (options.capacity) {
|
||||
this.setCapacity(options.capacity);
|
||||
}
|
||||
return makeArrayIndexableVector(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,8 +10,9 @@ describe('ConstantVector', () => {
|
||||
expect(v.get(1)).toEqual(value);
|
||||
|
||||
// Now check all of them
|
||||
for (let i = 0; i < 10; i++) {
|
||||
for (let i = 0; i < 7; i++) {
|
||||
expect(v.get(i)).toEqual(value);
|
||||
expect(v[i]).toEqual(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,28 +1,10 @@
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use a simple Arrays
|
||||
* @deprecated use a simple Arrays. NOTE: Not used in grafana core.
|
||||
*/
|
||||
export class ConstantVector<T = any> extends FunctionalVector<T> {
|
||||
constructor(private value: T, private len: number) {
|
||||
export class ConstantVector<T = any> extends Array<T> {
|
||||
constructor(value: T, len: number) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.len;
|
||||
}
|
||||
|
||||
get(index: number): T {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
toArray(): T[] {
|
||||
const arr = new Array<T>(this.length);
|
||||
return arr.fill(this.value);
|
||||
}
|
||||
|
||||
toJSON(): T[] {
|
||||
return this.toArray();
|
||||
return new Array<T>(len).fill(value) as ConstantVector<T>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,13 @@ import { DisplayProcessor } from '../types';
|
||||
import { Vector } from '../types/vector';
|
||||
import { formattedValueToString } from '../valueFormats';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use a simple Arrays
|
||||
* @deprecated use a simple Arrays. NOTE: not used in grafana core.
|
||||
*/
|
||||
export class FormattedVector<T = any> extends FunctionalVector<string> {
|
||||
constructor(private source: Vector<T>, private formatter: DisplayProcessor) {
|
||||
export class FormattedVector<T = any> extends Array<string> {
|
||||
constructor(source: Vector<T>, formatter: DisplayProcessor) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.source.length;
|
||||
}
|
||||
|
||||
get(index: number): string {
|
||||
const v = this.source.get(index);
|
||||
return formattedValueToString(this.formatter(v));
|
||||
return source.map((v) => formattedValueToString(formatter(v))) as FormattedVector<T>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
import { Field, FieldType } from '../types';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
|
||||
/**
|
||||
* IndexVector is a simple vector implementation that returns the index value
|
||||
* for each element in the vector. It is functionally equivolant a vector backed
|
||||
* by an array with values: `[0,1,2,...,length-1]`
|
||||
*
|
||||
* @deprecated use a simple Arrays
|
||||
* @deprecated use a simple Arrays. NOTE: not used in grafana core
|
||||
*/
|
||||
export class IndexVector extends FunctionalVector<number> {
|
||||
constructor(private len: number) {
|
||||
export class IndexVector extends Array<number> {
|
||||
constructor(len: number) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.len;
|
||||
}
|
||||
|
||||
get(index: number): number {
|
||||
return index;
|
||||
const arr = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
arr[i] = i;
|
||||
}
|
||||
return arr as IndexVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a field representing the range [0 ... length-1]
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
static newField(len: number): Field<number> {
|
||||
return {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Vector } from '../types';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
/**
|
||||
* RowVector makes the row values look like a vector
|
||||
* @internal
|
||||
* @deprecated use a simple Arrays
|
||||
*/
|
||||
export class RowVector extends FunctionalVector<number> {
|
||||
constructor(private columns: Vector[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
rowIndex = 0;
|
||||
|
||||
get length(): number {
|
||||
return this.columns.length;
|
||||
}
|
||||
|
||||
get(index: number): number {
|
||||
return this.columns[index].get(this.rowIndex);
|
||||
}
|
||||
|
||||
toArray(): number[] {
|
||||
return vectorToArray(this);
|
||||
}
|
||||
|
||||
toJSON(): number[] {
|
||||
return vectorToArray(this);
|
||||
}
|
||||
}
|
||||
13
packages/grafana-data/src/vector/SortedVector.test.ts
Normal file
13
packages/grafana-data/src/vector/SortedVector.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ArrayVector } from './ArrayVector';
|
||||
import { SortedVector } from './SortedVector';
|
||||
|
||||
describe('SortedVector', () => {
|
||||
it('Should support sorting', () => {
|
||||
const values = new ArrayVector([1, 5, 2, 4]);
|
||||
const sorted = new SortedVector(values, [0, 2, 3, 1]);
|
||||
expect(sorted.toArray()).toEqual([1, 2, 4, 5]);
|
||||
|
||||
// The proxy should still be an instance of SortedVector (used in timeseries)
|
||||
expect(sorted instanceof SortedVector).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Vector } from '../types/vector';
|
||||
import { makeArrayIndexableVector, Vector } from '../types/vector';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
@@ -11,6 +11,7 @@ import { vectorToArray } from './vectorToArray';
|
||||
export class SortedVector<T = any> extends FunctionalVector<T> {
|
||||
constructor(private source: Vector<T>, private order: number[]) {
|
||||
super();
|
||||
return makeArrayIndexableVector(this);
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
|
||||
Reference in New Issue
Block a user