FieldValues: Implement array accessors for deprecated Vector types (#66807)

This commit is contained in:
Ryan McKinley
2023-04-18 14:07:27 -07:00
committed by GitHub
parent 9452c0d718
commit 987eff82a3
18 changed files with 137 additions and 144 deletions

View File

@@ -270,8 +270,11 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
], ],
"packages/grafana-data/src/transformations/transformers/calculateField.ts:5381": [ "packages/grafana-data/src/transformations/transformers/calculateField.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"] [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
], ],
"packages/grafana-data/src/transformations/transformers/ensureColumns.ts:5381": [ "packages/grafana-data/src/transformations/transformers/ensureColumns.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@@ -497,7 +500,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "8"], [0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"], [0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"] [0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Do not use any type assertions.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"]
], ],
"packages/grafana-data/src/utils/OptionsUIBuilders.ts:5381": [ "packages/grafana-data/src/utils/OptionsUIBuilders.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -664,15 +669,23 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"] [0, 0, 0, "Unexpected any. Specify a different type.", "2"]
], ],
"packages/grafana-data/src/vector/AsNumberVector.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"packages/grafana-data/src/vector/BinaryOperationVector.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"packages/grafana-data/src/vector/CircularVector.ts:5381": [ "packages/grafana-data/src/vector/CircularVector.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"] [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
], ],
"packages/grafana-data/src/vector/ConstantVector.ts:5381": [ "packages/grafana-data/src/vector/ConstantVector.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
], ],
"packages/grafana-data/src/vector/FormattedVector.ts:5381": [ "packages/grafana-data/src/vector/FormattedVector.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
], ],
"packages/grafana-data/src/vector/FunctionalVector.ts:5381": [ "packages/grafana-data/src/vector/FunctionalVector.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -687,6 +700,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"] [0, 0, 0, "Unexpected any. Specify a different type.", "10"]
], ],
"packages/grafana-data/src/vector/IndexVector.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"packages/grafana-data/src/vector/SortedVector.ts:5381": [ "packages/grafana-data/src/vector/SortedVector.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
], ],

View File

@@ -3,11 +3,8 @@ import { map } from 'rxjs/operators';
import { getTimeField } from '../../dataframe/processDataFrame'; import { getTimeField } from '../../dataframe/processDataFrame';
import { getFieldDisplayName } from '../../field'; 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 { 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 { doStandardCalcs, fieldReducers, ReducerID } from '../fieldReducer';
import { getFieldMatcher } from '../matchers'; import { getFieldMatcher } from '../matchers';
import { FieldMatcherID } from '../matchers/ids'; import { FieldMatcherID } from '../matchers/ids';
@@ -61,7 +58,7 @@ export interface CalculateFieldTransformerOptions {
// TODO: config?: FieldConfig; or maybe field overrides? since the UI exists // 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> = { export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransformerOptions> = {
id: DataTransformerID.calculateField, id: DataTransformerID.calculateField,
@@ -181,7 +178,7 @@ function getReduceRowCreator(options: ReduceOptions, allFrames: DataFrame[]): Va
return (frame: DataFrame) => { return (frame: DataFrame) => {
// Find the columns that should be examined // Find the columns that should be examined
const columns: Vector[] = []; const columns: any[] = [];
for (const field of frame.fields) { for (const field of frame.fields) {
if (matcher(field, frame, allFrames)) { if (matcher(field, frame, allFrames)) {
columns.push(field.values); columns.push(field.values);
@@ -189,26 +186,31 @@ function getReduceRowCreator(options: ReduceOptions, allFrames: DataFrame[]): Va
} }
// Prepare a "fake" field for the row // Prepare a "fake" field for the row
const iter = new RowVector(columns); const size = columns.length;
const row: Field = { const row: Field = {
name: 'temp', name: 'temp',
values: iter, values: new Array(size),
type: FieldType.number, type: FieldType.number,
config: {}, config: {},
}; };
const vals: number[] = []; const vals: number[] = [];
for (let i = 0; i < frame.length; i++) { for (let i = 0; i < frame.length; i++) {
iter.rowIndex = i; for (let j = 0; j < size; j++) {
const val = reducer(row, ignoreNulls, nullAsZero)[options.reducer]; row.values[j] = columns[j][i];
vals.push(val); }
vals.push(reducer(row, ignoreNulls, nullAsZero)[options.reducer]);
} }
return vals; return vals;
}; };
} }
function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFrames: DataFrame[]): Vector | undefined { function findFieldValuesWithNameOrConstant(
frame: DataFrame,
name: string,
allFrames: DataFrame[]
): number[] | undefined {
if (!name) { if (!name) {
return undefined; return undefined;
} }
@@ -216,7 +218,7 @@ function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFr
for (const f of frame.fields) { for (const f of frame.fields) {
if (name === getFieldDisplayName(f, frame, allFrames)) { if (name === getFieldDisplayName(f, frame, allFrames)) {
if (f.type === FieldType.boolean) { if (f.type === FieldType.boolean) {
return new AsNumberVector(f.values); return f.values.map((v) => (v ? 1 : 0));
} }
return f.values; return f.values;
} }
@@ -224,7 +226,7 @@ function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFr
const v = parseFloat(name); const v = parseFloat(name);
if (!isNaN(v)) { if (!isNaN(v)) {
return new ConstantVector(v, frame.length); return new Array(frame.length).fill(v);
} }
return undefined; return undefined;
@@ -237,10 +239,14 @@ function getBinaryCreator(options: BinaryOptions, allFrames: DataFrame[]): Value
const left = findFieldValuesWithNameOrConstant(frame, options.left, allFrames); const left = findFieldValuesWithNameOrConstant(frame, options.left, allFrames);
const right = findFieldValuesWithNameOrConstant(frame, options.right, allFrames); const right = findFieldValuesWithNameOrConstant(frame, options.right, allFrames);
if (!left || !right || !operator) { 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;
}; };
} }

View File

@@ -147,7 +147,7 @@ function fieldToNumberField(field: Field): Field {
for (let n = 0; n < numValues.length; n++) { for (let n = 0; n < numValues.length; n++) {
let toBeConverted = numValues[n]; let toBeConverted = numValues[n];
if (valuesAsStrings) { if (valuesAsStrings && toBeConverted != null) {
// some numbers returned from datasources have commas // some numbers returned from datasources have commas
// strip the commas, coerce the string to a number // strip the commas, coerce the string to a number
toBeConverted = toBeConverted.replace(/,/g, ''); toBeConverted = toBeConverted.replace(/,/g, '');

View File

@@ -97,3 +97,30 @@ export interface ReadWriteVector<T = any> extends Vector<T> {}
* @deprecated -- this is now part of the base Vector interface * @deprecated -- this is now part of the base Vector interface
*/ */
export interface MutableVector<T = any> extends ReadWriteVector<T> {} 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;
}

View File

@@ -8,6 +8,9 @@ describe('Check Appending Vector', () => {
appended.append(new ArrayVector([4, 5, 6])); appended.append(new ArrayVector([4, 5, 6]));
appended.append(new ArrayVector([7, 8, 9])); appended.append(new ArrayVector([7, 8, 9]));
expect(appended.length).toEqual(9); expect(appended.length).toEqual(9);
expect(appended[0]).toEqual(1);
expect(appended[1]).toEqual(2);
expect(appended[100]).toEqual(undefined);
appended.setLength(5); appended.setLength(5);
expect(appended.length).toEqual(5); expect(appended.length).toEqual(5);

View File

@@ -1,4 +1,4 @@
import { Vector } from '../types/vector'; import { Vector, makeArrayIndexableVector } from '../types/vector';
import { FunctionalVector } from './FunctionalVector'; import { FunctionalVector } from './FunctionalVector';
import { vectorToArray } from './vectorToArray'; 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 * RAM -- rather than allocate a new array the size of all previous arrays, this just
* points the correct index to their original array values * 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> { export class AppendedVectors<T = any> extends FunctionalVector<T> {
length = 0; length = 0;
@@ -23,6 +23,7 @@ export class AppendedVectors<T = any> extends FunctionalVector<T> {
constructor(startAt = 0) { constructor(startAt = 0) {
super(); super();
this.length = startAt; this.length = startAt;
return makeArrayIndexableVector(this);
} }
/** /**

View File

@@ -1,23 +1,14 @@
import { Vector } from '../types'; import { Vector } from '../types';
import { FunctionalVector } from './FunctionalVector';
/** /**
* This will force all values to be numbers * This will force all values to be numbers
* *
* @public * @public
* @deprecated use a simple Arrays * @deprecated use a simple Arrays. NOTE: Not used in grafana core
*/ */
export class AsNumberVector extends FunctionalVector<number> { export class AsNumberVector extends Array<number> {
constructor(private field: Vector) { constructor(field: Vector) {
super(); super();
} return field.map((v) => +v) as AsNumberVector;
get length() {
return this.field.length;
}
get(index: number) {
return +this.field.get(index);
} }
} }

View File

@@ -11,9 +11,13 @@ describe('ScaledVector', () => {
const operation = binaryOperators.get(BinaryOperationID.Multiply).operation; const operation = binaryOperators.get(BinaryOperationID.Multiply).operation;
const v = new BinaryOperationVector(source, new ConstantVector(scale, source.length), operation); const v = new BinaryOperationVector(source, new ConstantVector(scale, source.length), operation);
expect(v.length).toEqual(source.length); expect(v.length).toEqual(source.length);
// expect(v.push(10)).toEqual(source.length); // not implemented // Accessed with getters
for (let i = 0; i < 10; i++) { for (let i = 0; i < 4; i++) {
expect(v.get(i)).toEqual(source.get(i) * scale); 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);
}
}); });
}); });

View File

@@ -1,31 +1,18 @@
import { Vector } from '../types/vector'; import { Vector } from '../types/vector';
import { BinaryOperation } from '../utils/binaryOperators'; import { BinaryOperation } from '../utils/binaryOperators';
import { FunctionalVector } from './FunctionalVector';
import { vectorToArray } from './vectorToArray';
/** /**
* @public * @public
* @deprecated use a simple Arrays * @deprecated use a simple Arrays. NOTE: Not used in grafana core
*/ */
export class BinaryOperationVector extends FunctionalVector<number> { export class BinaryOperationVector extends Array<number> {
constructor(private left: Vector<number>, private right: Vector<number>, private operation: BinaryOperation) { constructor(left: Vector<number>, right: Vector<number>, operation: BinaryOperation) {
super(); super();
}
get length(): number { const arr = new Array(left.length);
return this.left.length; for (let i = 0; i < arr.length; i++) {
} arr[i] = operation(left[i], right[i]);
}
get(index: number): number { return arr as BinaryOperationVector;
return this.operation(this.left.get(index), this.right.get(index));
}
toArray(): number[] {
return vectorToArray(this);
}
toJSON(): number[] {
return vectorToArray(this);
} }
} }

View File

@@ -5,6 +5,10 @@ describe('Check Circular Vector', () => {
const buffer = [1, 2, 3]; const buffer = [1, 2, 3];
const v = new CircularVector({ buffer }); // tail is default option const v = new CircularVector({ buffer }); // tail is default option
expect(v.toArray()).toEqual([1, 2, 3]); 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); v.add(4);
expect(v.toArray()).toEqual([2, 3, 4]); expect(v.toArray()).toEqual([2, 3, 4]);

View File

@@ -1,3 +1,5 @@
import { makeArrayIndexableVector } from '../types';
import { FunctionalVector } from './FunctionalVector'; import { FunctionalVector } from './FunctionalVector';
interface CircularOptions<T> { interface CircularOptions<T> {
@@ -34,6 +36,7 @@ export class CircularVector<T = any> extends FunctionalVector<T> {
if (options.capacity) { if (options.capacity) {
this.setCapacity(options.capacity); this.setCapacity(options.capacity);
} }
return makeArrayIndexableVector(this);
} }
/** /**

View File

@@ -10,8 +10,9 @@ describe('ConstantVector', () => {
expect(v.get(1)).toEqual(value); expect(v.get(1)).toEqual(value);
// Now check all of them // 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.get(i)).toEqual(value);
expect(v[i]).toEqual(value);
} }
}); });
}); });

View File

@@ -1,28 +1,10 @@
import { FunctionalVector } from './FunctionalVector';
/** /**
* @public * @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> { export class ConstantVector<T = any> extends Array<T> {
constructor(private value: T, private len: number) { constructor(value: T, len: number) {
super(); super();
} return new Array<T>(len).fill(value) as ConstantVector<T>;
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();
} }
} }

View File

@@ -2,23 +2,13 @@ import { DisplayProcessor } from '../types';
import { Vector } from '../types/vector'; import { Vector } from '../types/vector';
import { formattedValueToString } from '../valueFormats'; import { formattedValueToString } from '../valueFormats';
import { FunctionalVector } from './FunctionalVector';
/** /**
* @public * @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> { export class FormattedVector<T = any> extends Array<string> {
constructor(private source: Vector<T>, private formatter: DisplayProcessor) { constructor(source: Vector<T>, formatter: DisplayProcessor) {
super(); super();
} return source.map((v) => formattedValueToString(formatter(v))) as FormattedVector<T>;
get length() {
return this.source.length;
}
get(index: number): string {
const v = this.source.get(index);
return formattedValueToString(this.formatter(v));
} }
} }

View File

@@ -1,29 +1,26 @@
import { Field, FieldType } from '../types'; import { Field, FieldType } from '../types';
import { FunctionalVector } from './FunctionalVector';
/** /**
* IndexVector is a simple vector implementation that returns the index value * IndexVector is a simple vector implementation that returns the index value
* for each element in the vector. It is functionally equivolant a vector backed * for each element in the vector. It is functionally equivolant a vector backed
* by an array with values: `[0,1,2,...,length-1]` * 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> { export class IndexVector extends Array<number> {
constructor(private len: number) { constructor(len: number) {
super(); super();
} const arr = new Array(len);
for (let i = 0; i < len; i++) {
get length() { arr[i] = i;
return this.len; }
} return arr as IndexVector;
get(index: number): number {
return index;
} }
/** /**
* Returns a field representing the range [0 ... length-1] * Returns a field representing the range [0 ... length-1]
*
* @deprecated
*/ */
static newField(len: number): Field<number> { static newField(len: number): Field<number> {
return { return {

View File

@@ -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);
}
}

View 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();
});
});

View File

@@ -1,4 +1,4 @@
import { Vector } from '../types/vector'; import { makeArrayIndexableVector, Vector } from '../types/vector';
import { FunctionalVector } from './FunctionalVector'; import { FunctionalVector } from './FunctionalVector';
import { vectorToArray } from './vectorToArray'; import { vectorToArray } from './vectorToArray';
@@ -11,6 +11,7 @@ import { vectorToArray } from './vectorToArray';
export class SortedVector<T = any> extends FunctionalVector<T> { export class SortedVector<T = any> extends FunctionalVector<T> {
constructor(private source: Vector<T>, private order: number[]) { constructor(private source: Vector<T>, private order: number[]) {
super(); super();
return makeArrayIndexableVector(this);
} }
get length(): number { get length(): number {