瀏覽代碼

grafana/data: Reorganise code (#19136)

* Organise data frame and vectors code

* Organise transformations

* Move dataframe utils to dataframe dir

* Organise datetime utils

* Organise text utils

* Organise logs utils

* Revert "Organise logs utils"

This reverts commit c24115c75558236770dfa2f151e81f71bcd9ddef.

* registry -> Registry

* Transformations reorg
Dominik Prokop 6 年之前
父節點
當前提交
5c0f424d1b
共有 76 個文件被更改,包括 811 次插入766 次删除
  1. 22 0
      packages/grafana-data/src/dataframe/CircularDataFrame.ts
  2. 4 4
      packages/grafana-data/src/dataframe/DataFrameView.test.ts
  3. 2 1
      packages/grafana-data/src/dataframe/DataFrameView.ts
  4. 18 82
      packages/grafana-data/src/dataframe/FieldCache.test.ts
  5. 78 0
      packages/grafana-data/src/dataframe/FieldCache.ts
  6. 66 0
      packages/grafana-data/src/dataframe/MutableDataFrame.test.ts
  7. 7 126
      packages/grafana-data/src/dataframe/MutableDataFrame.ts
  8. 5 0
      packages/grafana-data/src/dataframe/index.ts
  9. 2 2
      packages/grafana-data/src/dataframe/processDataFrame.test.ts
  10. 5 4
      packages/grafana-data/src/dataframe/processDataFrame.ts
  11. 1 1
      packages/grafana-data/src/datetime/datemath.test.ts
  12. 1 1
      packages/grafana-data/src/datetime/datemath.ts
  13. 5 0
      packages/grafana-data/src/datetime/index.ts
  14. 0 0
      packages/grafana-data/src/datetime/moment_wrapper.ts
  15. 1 1
      packages/grafana-data/src/datetime/rangeutil.ts
  16. 7 2
      packages/grafana-data/src/index.ts
  17. 3 0
      packages/grafana-data/src/text/index.ts
  18. 0 0
      packages/grafana-data/src/text/markdown.test.ts
  19. 0 0
      packages/grafana-data/src/text/markdown.ts
  20. 0 0
      packages/grafana-data/src/text/string.test.ts
  21. 0 0
      packages/grafana-data/src/text/string.ts
  22. 0 0
      packages/grafana-data/src/text/text.test.ts
  23. 0 0
      packages/grafana-data/src/text/text.ts
  24. 3 3
      packages/grafana-data/src/transformations/fieldReducer.test.ts
  25. 2 2
      packages/grafana-data/src/transformations/fieldReducer.ts
  26. 7 0
      packages/grafana-data/src/transformations/index.ts
  27. 12 23
      packages/grafana-data/src/transformations/matchers.ts
  28. 2 2
      packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.test.ts
  29. 1 1
      packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.ts
  30. 0 0
      packages/grafana-data/src/transformations/matchers/ids.ts
  31. 1 1
      packages/grafana-data/src/transformations/matchers/matchers.test.ts
  32. 2 2
      packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts
  33. 2 2
      packages/grafana-data/src/transformations/matchers/nameMatcher.ts
  34. 2 1
      packages/grafana-data/src/transformations/matchers/predicates.test.ts
  35. 2 9
      packages/grafana-data/src/transformations/matchers/predicates.ts
  36. 1 1
      packages/grafana-data/src/transformations/matchers/refIdMatcher.ts
  37. 7 7
      packages/grafana-data/src/transformations/transformers.test.ts
  38. 11 25
      packages/grafana-data/src/transformations/transformers.ts
  39. 0 0
      packages/grafana-data/src/transformations/transformers/__snapshots__/reduce.test.ts.snap
  40. 4 3
      packages/grafana-data/src/transformations/transformers/append.test.ts
  41. 2 2
      packages/grafana-data/src/transformations/transformers/append.ts
  42. 3 3
      packages/grafana-data/src/transformations/transformers/filter.test.ts
  43. 3 3
      packages/grafana-data/src/transformations/transformers/filter.ts
  44. 3 2
      packages/grafana-data/src/transformations/transformers/filterByName.test.ts
  45. 2 2
      packages/grafana-data/src/transformations/transformers/filterByName.ts
  46. 0 0
      packages/grafana-data/src/transformations/transformers/ids.ts
  47. 1 1
      packages/grafana-data/src/transformations/transformers/noop.ts
  48. 2 2
      packages/grafana-data/src/transformations/transformers/reduce.test.ts
  49. 6 6
      packages/grafana-data/src/transformations/transformers/reduce.ts
  50. 2 20
      packages/grafana-data/src/types/dataFrame.ts
  51. 1 0
      packages/grafana-data/src/types/index.ts
  52. 1 1
      packages/grafana-data/src/types/time.ts
  53. 32 0
      packages/grafana-data/src/types/transformations.ts
  54. 40 0
      packages/grafana-data/src/types/vector.ts
  55. 0 0
      packages/grafana-data/src/utils/Registry.ts
  56. 2 2
      packages/grafana-data/src/utils/csv.test.ts
  57. 2 2
      packages/grafana-data/src/utils/csv.ts
  58. 28 0
      packages/grafana-data/src/utils/fieldParser.ts
  59. 1 20
      packages/grafana-data/src/utils/index.ts
  60. 1 1
      packages/grafana-data/src/utils/logs.ts
  61. 0 338
      packages/grafana-data/src/utils/vector.ts
  62. 23 0
      packages/grafana-data/src/vector/AppendedVectors.test.ts
  63. 73 0
      packages/grafana-data/src/vector/AppendedVectors.ts
  64. 37 0
      packages/grafana-data/src/vector/ArrayVector.ts
  65. 1 49
      packages/grafana-data/src/vector/CircularVector.test.ts
  66. 138 0
      packages/grafana-data/src/vector/CircularVector.ts
  67. 17 0
      packages/grafana-data/src/vector/ConstantVector.test.ts
  68. 22 0
      packages/grafana-data/src/vector/ConstantVector.ts
  69. 15 0
      packages/grafana-data/src/vector/ScaledVector.test.ts
  70. 22 0
      packages/grafana-data/src/vector/ScaledVector.ts
  71. 25 0
      packages/grafana-data/src/vector/SortedVector.ts
  72. 6 0
      packages/grafana-data/src/vector/index.ts
  73. 9 0
      packages/grafana-data/src/vector/vectorToArray.ts
  74. 2 2
      packages/grafana-ui/src/components/TransformersUI/FilterByNameTransformerEditor.tsx
  75. 2 3
      packages/grafana-ui/src/components/TransformersUI/ReduceTransformerEditor.tsx
  76. 1 1
      public/app/features/explore/utils/ResultProcessor.test.ts

+ 22 - 0
packages/grafana-data/src/dataframe/CircularDataFrame.ts

@@ -0,0 +1,22 @@
+import { MutableDataFrame } from './MutableDataFrame';
+import { CircularVector } from '../vector/CircularVector';
+
+interface CircularOptions {
+  append?: 'head' | 'tail';
+  capacity?: number;
+}
+
+/**
+ * This dataframe can have values constantly added, and will never
+ * exceed the given capacity
+ */
+export class CircularDataFrame<T = any> extends MutableDataFrame<T> {
+  constructor(options: CircularOptions) {
+    super(undefined, (buffer?: any[]) => {
+      return new CircularVector({
+        ...options,
+        buffer,
+      });
+    });
+  }
+}

+ 4 - 4
packages/grafana-data/src/utils/dataFrameView.test.ts → packages/grafana-data/src/dataframe/DataFrameView.test.ts

@@ -1,7 +1,7 @@
-import { FieldType, DataFrameDTO } from '../types/index';
-import { MutableDataFrame } from './dataFrameHelper';
-import { DataFrameView } from './dataFrameView';
-import { DateTime } from './moment_wrapper';
+import { FieldType, DataFrameDTO } from '../types/dataFrame';
+import { DateTime } from '../datetime/moment_wrapper';
+import { MutableDataFrame } from './MutableDataFrame';
+import { DataFrameView } from './DataFrameView';
 
 interface MySpecialObject {
   time: DateTime;

+ 2 - 1
packages/grafana-data/src/utils/dataFrameView.ts → packages/grafana-data/src/dataframe/DataFrameView.ts

@@ -1,4 +1,5 @@
-import { DataFrame, Vector } from '../types/index';
+import { Vector } from '../types/vector';
+import { DataFrame } from '../types/dataFrame';
 
 /**
  * This abstraction will present the contents of a DataFrame as if

+ 18 - 82
packages/grafana-data/src/utils/dataFrameHelper.test.ts → packages/grafana-data/src/dataframe/FieldCache.test.ts

@@ -1,30 +1,7 @@
-import { DataFrameDTO, FieldType } from '../types';
-import { FieldCache, MutableDataFrame } from './dataFrameHelper';
+import { FieldCache } from './FieldCache';
+import { FieldType } from '../types/dataFrame';
 import { toDataFrame } from './processDataFrame';
 
-describe('dataFrameHelper', () => {
-  const frame = toDataFrame({
-    fields: [
-      { name: 'time', type: FieldType.time, values: [100, 200, 300] },
-      { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
-      { name: 'value', type: FieldType.number, values: [1, 2, 3] },
-      { name: 'value', type: FieldType.number, values: [4, 5, 6] },
-    ],
-  });
-  const ext = new FieldCache(frame);
-
-  it('should get the first field with a duplicate name', () => {
-    const field = ext.getFieldByName('value');
-    expect(field!.name).toEqual('value');
-    expect(field!.values.toJSON()).toEqual([1, 2, 3]);
-  });
-
-  it('should return index of the field', () => {
-    const field = ext.getFirstFieldOfType(FieldType.number);
-    expect(field!.index).toEqual(2);
-  });
-});
-
 describe('FieldCache', () => {
   it('when creating a new FieldCache from fields should be able to query cache', () => {
     const frame = toDataFrame({
@@ -90,68 +67,27 @@ describe('FieldCache', () => {
     expect(fieldCache.getFieldByName('undefined')!.name).toEqual(expectedFieldNames[5]);
     expect(fieldCache.getFieldByName('null')).toBeUndefined();
   });
-});
-
-describe('reverse', () => {
-  describe('when called with a DataFrame', () => {
-    it('then it should reverse the order of values in all fields', () => {
-      const frame: DataFrameDTO = {
-        fields: [
-          { name: 'time', type: FieldType.time, values: [100, 200, 300] },
-          { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
-          { name: 'value', type: FieldType.number, values: [1, 2, 3] },
-        ],
-      };
-
-      const helper = new MutableDataFrame(frame);
-
-      expect(helper.values.time.toArray()).toEqual([100, 200, 300]);
-      expect(helper.values.name.toArray()).toEqual(['a', 'b', 'c']);
-      expect(helper.values.value.toArray()).toEqual([1, 2, 3]);
-
-      helper.reverse();
-
-      expect(helper.values.time.toArray()).toEqual([300, 200, 100]);
-      expect(helper.values.name.toArray()).toEqual(['c', 'b', 'a']);
-      expect(helper.values.value.toArray()).toEqual([3, 2, 1]);
-    });
-  });
-});
 
-describe('Apending DataFrame', () => {
-  it('Should append values', () => {
-    const dto: DataFrameDTO = {
+  describe('field retrieval', () => {
+    const frame = toDataFrame({
       fields: [
-        { name: 'time', type: FieldType.time, values: [100] },
-        { name: 'name', type: FieldType.string, values: ['a', 'b'] },
+        { name: 'time', type: FieldType.time, values: [100, 200, 300] },
+        { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
         { name: 'value', type: FieldType.number, values: [1, 2, 3] },
+        { name: 'value', type: FieldType.number, values: [4, 5, 6] },
       ],
-    };
-
-    const frame = new MutableDataFrame(dto);
-    expect(frame.values.time.toArray()).toEqual([100, null, null]);
-
-    // Set a value on the second row
-    frame.set(1, { time: 200, name: 'BB', value: 20 });
-    expect(frame.toArray()).toEqual([
-      { time: 100, name: 'a', value: 1 }, // 1
-      { time: 200, name: 'BB', value: 20 }, // 2
-      { time: null, name: null, value: 3 }, // 3
-    ]);
+    });
+    const ext = new FieldCache(frame);
 
-    // Set a value on the second row
-    frame.add({ value2: 'XXX' }, true);
-    expect(frame.toArray()).toEqual([
-      { time: 100, name: 'a', value: 1, value2: null }, // 1
-      { time: 200, name: 'BB', value: 20, value2: null }, // 2
-      { time: null, name: null, value: 3, value2: null }, // 3
-      { time: null, name: null, value: null, value2: 'XXX' }, // 4
-    ]);
+    it('should get the first field with a duplicate name', () => {
+      const field = ext.getFieldByName('value');
+      expect(field!.name).toEqual('value');
+      expect(field!.values.toJSON()).toEqual([1, 2, 3]);
+    });
 
-    // Make sure length survives a spread operator
-    const keys = Object.keys(frame);
-    const copy = { ...frame } as any;
-    expect(keys).toContain('length');
-    expect(copy.length).toEqual(frame.length);
+    it('should return index of the field', () => {
+      const field = ext.getFirstFieldOfType(FieldType.number);
+      expect(field!.index).toEqual(2);
+    });
   });
 });

+ 78 - 0
packages/grafana-data/src/dataframe/FieldCache.ts

@@ -0,0 +1,78 @@
+import { Field, DataFrame, FieldType, guessFieldTypeForField } from '../index';
+
+interface FieldWithIndex extends Field {
+  index: number;
+}
+
+export class FieldCache {
+  fields: FieldWithIndex[] = [];
+
+  private fieldByName: { [key: string]: FieldWithIndex } = {};
+  private fieldByType: { [key: string]: FieldWithIndex[] } = {};
+
+  constructor(data: DataFrame) {
+    this.fields = data.fields.map((field, idx) => ({
+      ...field,
+      index: idx,
+    }));
+
+    for (let i = 0; i < data.fields.length; i++) {
+      const field = data.fields[i];
+      // Make sure it has a type
+      if (field.type === FieldType.other) {
+        const t = guessFieldTypeForField(field);
+        if (t) {
+          field.type = t;
+        }
+      }
+      if (!this.fieldByType[field.type]) {
+        this.fieldByType[field.type] = [];
+      }
+      this.fieldByType[field.type].push({
+        ...field,
+        index: i,
+      });
+
+      if (this.fieldByName[field.name]) {
+        console.warn('Duplicate field names in DataFrame: ', field.name);
+      } else {
+        this.fieldByName[field.name] = { ...field, index: i };
+      }
+    }
+  }
+
+  getFields(type?: FieldType): FieldWithIndex[] {
+    if (!type) {
+      return [...this.fields]; // All fields
+    }
+    const fields = this.fieldByType[type];
+    if (fields) {
+      return [...fields];
+    }
+    return [];
+  }
+
+  hasFieldOfType(type: FieldType): boolean {
+    const types = this.fieldByType[type];
+    return types && types.length > 0;
+  }
+
+  getFirstFieldOfType(type: FieldType): FieldWithIndex | undefined {
+    const arr = this.fieldByType[type];
+    if (arr && arr.length > 0) {
+      return arr[0];
+    }
+    return undefined;
+  }
+
+  hasFieldNamed(name: string): boolean {
+    return !!this.fieldByName[name];
+  }
+
+  /**
+   * Returns the first field with the given name.
+   */
+  getFieldByName(name: string): FieldWithIndex | undefined {
+    return this.fieldByName[name];
+  }
+}

+ 66 - 0
packages/grafana-data/src/dataframe/MutableDataFrame.test.ts

@@ -0,0 +1,66 @@
+import { DataFrameDTO, FieldType } from '../types/dataFrame';
+import { MutableDataFrame } from './MutableDataFrame';
+
+describe('Reversing DataFrame', () => {
+  describe('when called with a DataFrame', () => {
+    it('then it should reverse the order of values in all fields', () => {
+      const frame: DataFrameDTO = {
+        fields: [
+          { name: 'time', type: FieldType.time, values: [100, 200, 300] },
+          { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
+          { name: 'value', type: FieldType.number, values: [1, 2, 3] },
+        ],
+      };
+
+      const helper = new MutableDataFrame(frame);
+
+      expect(helper.values.time.toArray()).toEqual([100, 200, 300]);
+      expect(helper.values.name.toArray()).toEqual(['a', 'b', 'c']);
+      expect(helper.values.value.toArray()).toEqual([1, 2, 3]);
+
+      helper.reverse();
+
+      expect(helper.values.time.toArray()).toEqual([300, 200, 100]);
+      expect(helper.values.name.toArray()).toEqual(['c', 'b', 'a']);
+      expect(helper.values.value.toArray()).toEqual([3, 2, 1]);
+    });
+  });
+});
+
+describe('Apending DataFrame', () => {
+  it('Should append values', () => {
+    const dto: DataFrameDTO = {
+      fields: [
+        { name: 'time', type: FieldType.time, values: [100] },
+        { name: 'name', type: FieldType.string, values: ['a', 'b'] },
+        { name: 'value', type: FieldType.number, values: [1, 2, 3] },
+      ],
+    };
+
+    const frame = new MutableDataFrame(dto);
+    expect(frame.values.time.toArray()).toEqual([100, null, null]);
+
+    // Set a value on the second row
+    frame.set(1, { time: 200, name: 'BB', value: 20 });
+    expect(frame.toArray()).toEqual([
+      { time: 100, name: 'a', value: 1 }, // 1
+      { time: 200, name: 'BB', value: 20 }, // 2
+      { time: null, name: null, value: 3 }, // 3
+    ]);
+
+    // Set a value on the second row
+    frame.add({ value2: 'XXX' }, true);
+    expect(frame.toArray()).toEqual([
+      { time: 100, name: 'a', value: 1, value2: null }, // 1
+      { time: 200, name: 'BB', value: 20, value2: null }, // 2
+      { time: null, name: null, value: 3, value2: null }, // 3
+      { time: null, name: null, value: null, value2: 'XXX' }, // 4
+    ]);
+
+    // Make sure length survives a spread operator
+    const keys = Object.keys(frame);
+    const copy = { ...frame } as any;
+    expect(keys).toContain('length');
+    expect(copy.length).toEqual(frame.length);
+  });
+});

+ 7 - 126
packages/grafana-data/src/utils/dataFrameHelper.ts → packages/grafana-data/src/dataframe/MutableDataFrame.ts

@@ -1,111 +1,12 @@
-import { Field, FieldType, DataFrame, Vector, FieldDTO, DataFrameDTO } from '../types/dataFrame';
-import { Labels, QueryResultMeta, KeyValue } from '../types/data';
-import { guessFieldTypeForField, guessFieldTypeFromValue, toDataFrameDTO } from './processDataFrame';
-import { ArrayVector, MutableVector, vectorToArray, CircularVector } from './vector';
+import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame';
+import { KeyValue, QueryResultMeta, Labels } from '../types/data';
+import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame';
 import isArray from 'lodash/isArray';
 import isString from 'lodash/isString';
-
-interface FieldWithIndex extends Field {
-  index: number;
-}
-export class FieldCache {
-  fields: FieldWithIndex[] = [];
-
-  private fieldByName: { [key: string]: FieldWithIndex } = {};
-  private fieldByType: { [key: string]: FieldWithIndex[] } = {};
-
-  constructor(data: DataFrame) {
-    this.fields = data.fields.map((field, idx) => ({
-      ...field,
-      index: idx,
-    }));
-
-    for (let i = 0; i < data.fields.length; i++) {
-      const field = data.fields[i];
-      // Make sure it has a type
-      if (field.type === FieldType.other) {
-        const t = guessFieldTypeForField(field);
-        if (t) {
-          field.type = t;
-        }
-      }
-      if (!this.fieldByType[field.type]) {
-        this.fieldByType[field.type] = [];
-      }
-      this.fieldByType[field.type].push({
-        ...field,
-        index: i,
-      });
-
-      if (this.fieldByName[field.name]) {
-        console.warn('Duplicate field names in DataFrame: ', field.name);
-      } else {
-        this.fieldByName[field.name] = { ...field, index: i };
-      }
-    }
-  }
-
-  getFields(type?: FieldType): FieldWithIndex[] {
-    if (!type) {
-      return [...this.fields]; // All fields
-    }
-    const fields = this.fieldByType[type];
-    if (fields) {
-      return [...fields];
-    }
-    return [];
-  }
-
-  hasFieldOfType(type: FieldType): boolean {
-    const types = this.fieldByType[type];
-    return types && types.length > 0;
-  }
-
-  getFirstFieldOfType(type: FieldType): FieldWithIndex | undefined {
-    const arr = this.fieldByType[type];
-    if (arr && arr.length > 0) {
-      return arr[0];
-    }
-    return undefined;
-  }
-
-  hasFieldNamed(name: string): boolean {
-    return !!this.fieldByName[name];
-  }
-
-  /**
-   * Returns the first field with the given name.
-   */
-  getFieldByName(name: string): FieldWithIndex | undefined {
-    return this.fieldByName[name];
-  }
-}
-
-function makeFieldParser(value: any, field: Field): (value: string) => any {
-  if (!field.type) {
-    if (field.name === 'time' || field.name === 'Time') {
-      field.type = FieldType.time;
-    } else {
-      field.type = guessFieldTypeFromValue(value);
-    }
-  }
-
-  if (field.type === FieldType.number) {
-    return (value: string) => {
-      return parseFloat(value);
-    };
-  }
-
-  // Will convert anything that starts with "T" to true
-  if (field.type === FieldType.boolean) {
-    return (value: string) => {
-      return !(value[0] === 'F' || value[0] === 'f' || value[0] === '0');
-    };
-  }
-
-  // Just pass the string back
-  return (value: string) => value;
-}
+import { makeFieldParser } from '../utils/fieldParser';
+import { MutableVector, Vector } from '../types/vector';
+import { ArrayVector } from '../vector/ArrayVector';
+import { vectorToArray } from '../vector/vectorToArray';
 
 export type MutableField<T = any> = Field<T, MutableVector<T>>;
 
@@ -380,23 +281,3 @@ export class MutableDataFrame<T = any> implements DataFrame, MutableVector<T> {
     return toDataFrameDTO(this);
   }
 }
-
-interface CircularOptions {
-  append?: 'head' | 'tail';
-  capacity?: number;
-}
-
-/**
- * This dataframe can have values constantly added, and will never
- * exceed the given capacity
- */
-export class CircularDataFrame<T = any> extends MutableDataFrame<T> {
-  constructor(options: CircularOptions) {
-    super(undefined, (buffer?: any[]) => {
-      return new CircularVector({
-        ...options,
-        buffer,
-      });
-    });
-  }
-}

+ 5 - 0
packages/grafana-data/src/dataframe/index.ts

@@ -0,0 +1,5 @@
+export * from './DataFrameView';
+export * from './FieldCache';
+export * from './CircularDataFrame';
+export * from './MutableDataFrame';
+export * from './processDataFrame';

+ 2 - 2
packages/grafana-data/src/utils/processDataFrame.test.ts → packages/grafana-data/src/dataframe/processDataFrame.test.ts

@@ -8,8 +8,8 @@ import {
   sortDataFrame,
 } from './processDataFrame';
 import { FieldType, TimeSeries, TableData, DataFrameDTO } from '../types/index';
-import { dateTime } from './moment_wrapper';
-import { MutableDataFrame } from './dataFrameHelper';
+import { dateTime } from '../datetime/moment_wrapper';
+import { MutableDataFrame } from './MutableDataFrame';
 
 describe('toDataFrame', () => {
   it('converts timeseries to series', () => {

+ 5 - 4
packages/grafana-data/src/utils/processDataFrame.ts → packages/grafana-data/src/dataframe/processDataFrame.ts

@@ -17,10 +17,11 @@ import {
   FieldDTO,
   DataFrameDTO,
 } from '../types/index';
-import { isDateTime } from './moment_wrapper';
-import { ArrayVector, SortedVector } from './vector';
-import { MutableDataFrame } from './dataFrameHelper';
-import { deprecationWarning } from './deprecationWarning';
+import { isDateTime } from '../datetime/moment_wrapper';
+import { deprecationWarning } from '../utils/deprecationWarning';
+import { ArrayVector } from '../vector/ArrayVector';
+import { MutableDataFrame } from './MutableDataFrame';
+import { SortedVector } from '../vector/SortedVector';
 
 function convertTableToDataFrame(table: TableData): DataFrame {
   const fields = table.columns.map(c => {

+ 1 - 1
packages/grafana-data/src/utils/datemath.test.ts → packages/grafana-data/src/datetime/datemath.test.ts

@@ -2,7 +2,7 @@ import sinon, { SinonFakeTimers } from 'sinon';
 import each from 'lodash/each';
 
 import * as dateMath from './datemath';
-import { dateTime, DurationUnit, DateTime } from '../utils/moment_wrapper';
+import { dateTime, DurationUnit, DateTime } from './moment_wrapper';
 
 describe('DateMath', () => {
   const spans: DurationUnit[] = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];

+ 1 - 1
packages/grafana-data/src/utils/datemath.ts → packages/grafana-data/src/datetime/datemath.ts

@@ -1,7 +1,7 @@
 import includes from 'lodash/includes';
 import isDate from 'lodash/isDate';
 import { DateTime, dateTime, dateTimeForTimeZone, ISO_8601, isDateTime, DurationUnit } from './moment_wrapper';
-import { TimeZone } from '../types';
+import { TimeZone } from '../types/index';
 
 const units: DurationUnit[] = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
 

+ 5 - 0
packages/grafana-data/src/datetime/index.ts

@@ -0,0 +1,5 @@
+// Names are too general to export globally
+import * as dateMath from './datemath';
+import * as rangeUtil from './rangeutil';
+export * from './moment_wrapper';
+export { dateMath, rangeUtil };

+ 0 - 0
packages/grafana-data/src/utils/moment_wrapper.ts → packages/grafana-data/src/datetime/moment_wrapper.ts


+ 1 - 1
packages/grafana-data/src/utils/rangeutil.ts → packages/grafana-data/src/datetime/rangeutil.ts

@@ -4,7 +4,7 @@ import groupBy from 'lodash/groupBy';
 import { RawTimeRange } from '../types/time';
 
 import * as dateMath from './datemath';
-import { isDateTime, DateTime } from '../utils/moment_wrapper';
+import { isDateTime, DateTime } from './moment_wrapper';
 
 const spans: { [key: string]: { display: string; section?: number } } = {
   s: { display: 'second' },

+ 7 - 2
packages/grafana-data/src/index.ts

@@ -1,2 +1,7 @@
-export * from './utils/index';
-export * from './types/index';
+export * from './utils';
+export * from './types';
+export * from './vector';
+export * from './dataframe';
+export * from './transformations';
+export * from './datetime';
+export * from './text';

+ 3 - 0
packages/grafana-data/src/text/index.ts

@@ -0,0 +1,3 @@
+export * from './string';
+export * from './markdown';
+export * from './text';

+ 0 - 0
packages/grafana-data/src/utils/markdown.test.ts → packages/grafana-data/src/text/markdown.test.ts


+ 0 - 0
packages/grafana-data/src/utils/markdown.ts → packages/grafana-data/src/text/markdown.ts


+ 0 - 0
packages/grafana-data/src/utils/string.test.ts → packages/grafana-data/src/text/string.test.ts


+ 0 - 0
packages/grafana-data/src/utils/string.ts → packages/grafana-data/src/text/string.ts


+ 0 - 0
packages/grafana-data/src/utils/text.test.ts → packages/grafana-data/src/text/text.test.ts


+ 0 - 0
packages/grafana-data/src/utils/text.ts → packages/grafana-data/src/text/text.ts


+ 3 - 3
packages/grafana-data/src/utils/fieldReducer.test.ts → packages/grafana-data/src/transformations/fieldReducer.test.ts

@@ -3,9 +3,9 @@ import difference from 'lodash/difference';
 import { fieldReducers, ReducerID, reduceField } from './fieldReducer';
 
 import { Field, FieldType } from '../types/index';
-import { MutableDataFrame } from './dataFrameHelper';
-import { ArrayVector } from './vector';
-import { guessFieldTypeFromValue } from './processDataFrame';
+import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
+import { MutableDataFrame } from '../dataframe/MutableDataFrame';
+import { ArrayVector } from '../vector/ArrayVector';
 
 /**
  * Run a reducer and get back the value

+ 2 - 2
packages/grafana-data/src/utils/fieldReducer.ts → packages/grafana-data/src/transformations/fieldReducer.ts

@@ -1,8 +1,8 @@
 // Libraries
 import isNumber from 'lodash/isNumber';
 
-import { NullValueMode, Field } from '../types';
-import { Registry, RegistryItem } from './registry';
+import { NullValueMode, Field } from '../types/index';
+import { Registry, RegistryItem } from '../utils/Registry';
 
 export enum ReducerID {
   sum = 'sum',

+ 7 - 0
packages/grafana-data/src/transformations/index.ts

@@ -0,0 +1,7 @@
+export * from './matchers/ids';
+export * from './transformers/ids';
+export * from './matchers';
+export * from './transformers';
+export * from './fieldReducer';
+export { FilterFieldsByNameTransformerOptions } from './transformers/filterByName';
+export { ReduceTransformerOptions } from './transformers/reduce';

+ 12 - 23
packages/grafana-data/src/utils/matchers/matchers.ts → packages/grafana-data/src/transformations/matchers.ts

@@ -1,27 +1,16 @@
-import { Field, DataFrame } from '../../types/dataFrame';
-import { Registry, RegistryItemWithOptions } from '../registry';
-
-export type FieldMatcher = (field: Field) => boolean;
-export type FrameMatcher = (frame: DataFrame) => boolean;
-
-export interface FieldMatcherInfo<TOptions = any> extends RegistryItemWithOptions<TOptions> {
-  get: (options: TOptions) => FieldMatcher;
-}
-
-export interface FrameMatcherInfo<TOptions = any> extends RegistryItemWithOptions<TOptions> {
-  get: (options: TOptions) => FrameMatcher;
-}
-
-export interface MatcherConfig<TOptions = any> {
-  id: string;
-  options?: TOptions;
-}
-
 // Load the Buildtin matchers
-import { getFieldPredicateMatchers, getFramePredicateMatchers } from './predicates';
-import { getFieldNameMatchers, getFrameNameMatchers } from './nameMatcher';
-import { getFieldTypeMatchers } from './fieldTypeMatcher';
-import { getRefIdMatchers } from './refIdMatcher';
+import { getFieldPredicateMatchers, getFramePredicateMatchers } from './matchers/predicates';
+import { getFieldNameMatchers, getFrameNameMatchers } from './matchers/nameMatcher';
+import { getFieldTypeMatchers } from './matchers/fieldTypeMatcher';
+import { getRefIdMatchers } from './matchers/refIdMatcher';
+import {
+  FieldMatcherInfo,
+  MatcherConfig,
+  FrameMatcherInfo,
+  FieldMatcher,
+  FrameMatcher,
+} from '../types/transformations';
+import { Registry } from '../utils/Registry';
 
 export const fieldMatchers = new Registry<FieldMatcherInfo>(() => {
   return [

+ 2 - 2
packages/grafana-data/src/utils/matchers/fieldTypeMatcher.test.ts → packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.test.ts

@@ -1,7 +1,7 @@
 import { FieldType } from '../../types/dataFrame';
-import { fieldMatchers } from './matchers';
+import { fieldMatchers } from '../matchers';
 import { FieldMatcherID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
 
 export const simpleSeriesWithTypes = toDataFrame({
   fields: [

+ 1 - 1
packages/grafana-data/src/utils/matchers/fieldTypeMatcher.ts → packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.ts

@@ -1,6 +1,6 @@
 import { Field, FieldType } from '../../types/dataFrame';
-import { FieldMatcherInfo } from './matchers';
 import { FieldMatcherID } from './ids';
+import { FieldMatcherInfo } from '../../types/transformations';
 
 // General Field matcher
 const fieldTypeMacher: FieldMatcherInfo<FieldType> = {

+ 0 - 0
packages/grafana-data/src/utils/matchers/ids.ts → packages/grafana-data/src/transformations/matchers/ids.ts


+ 1 - 1
packages/grafana-data/src/utils/matchers/matchers.test.ts → packages/grafana-data/src/transformations/matchers/matchers.test.ts

@@ -1,4 +1,4 @@
-import { fieldMatchers } from './matchers';
+import { fieldMatchers } from '../matchers';
 import { FieldMatcherID } from './ids';
 
 describe('Matchers', () => {

+ 2 - 2
packages/grafana-data/src/utils/matchers/nameMatcher.test.ts → packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts

@@ -1,6 +1,6 @@
-import { getFieldMatcher } from './matchers';
+import { getFieldMatcher } from '../matchers';
 import { FieldMatcherID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
 
 describe('Field Name Matcher', () => {
   it('Match all with wildcard regex', () => {

+ 2 - 2
packages/grafana-data/src/utils/matchers/nameMatcher.ts → packages/grafana-data/src/transformations/matchers/nameMatcher.ts

@@ -1,7 +1,7 @@
 import { Field, DataFrame } from '../../types/dataFrame';
-import { FieldMatcherInfo, FrameMatcherInfo } from './matchers';
 import { FieldMatcherID, FrameMatcherID } from './ids';
-import { stringToJsRegex } from '../string';
+import { FieldMatcherInfo, FrameMatcherInfo } from '../../types/transformations';
+import { stringToJsRegex } from '../../text/string';
 
 // General Field matcher
 const fieldNameMacher: FieldMatcherInfo<string> = {

+ 2 - 1
packages/grafana-data/src/utils/matchers/predicates.test.ts → packages/grafana-data/src/transformations/matchers/predicates.test.ts

@@ -1,7 +1,8 @@
 import { FieldType } from '../../types/dataFrame';
-import { MatcherConfig, fieldMatchers } from './matchers';
+import { fieldMatchers } from '../matchers';
 import { simpleSeriesWithTypes } from './fieldTypeMatcher.test';
 import { FieldMatcherID, MatcherID } from './ids';
+import { MatcherConfig } from '../../types/transformations';
 
 const matchesNumberConfig: MatcherConfig = {
   id: FieldMatcherID.byType,

+ 2 - 9
packages/grafana-data/src/utils/matchers/predicates.ts → packages/grafana-data/src/transformations/matchers/predicates.ts

@@ -1,14 +1,7 @@
 import { Field, DataFrame } from '../../types/dataFrame';
 import { MatcherID } from './ids';
-import {
-  FrameMatcherInfo,
-  FieldMatcherInfo,
-  MatcherConfig,
-  getFieldMatcher,
-  fieldMatchers,
-  getFrameMatchers,
-  frameMatchers,
-} from './matchers';
+import { getFieldMatcher, fieldMatchers, getFrameMatchers, frameMatchers } from '../matchers';
+import { FieldMatcherInfo, MatcherConfig, FrameMatcherInfo } from '../../types/transformations';
 
 const anyFieldMatcher: FieldMatcherInfo<MatcherConfig[]> = {
   id: MatcherID.anyMatch,

+ 1 - 1
packages/grafana-data/src/utils/matchers/refIdMatcher.ts → packages/grafana-data/src/transformations/matchers/refIdMatcher.ts

@@ -1,6 +1,6 @@
 import { DataFrame } from '../../types/dataFrame';
-import { FrameMatcherInfo } from './matchers';
 import { FrameMatcherID } from './ids';
+import { FrameMatcherInfo } from '../../types/transformations';
 
 // General Field matcher
 const refIdMacher: FrameMatcherInfo<string> = {

+ 7 - 7
packages/grafana-data/src/utils/transformers/transformers.test.ts → packages/grafana-data/src/transformations/transformers.test.ts

@@ -1,13 +1,13 @@
-import { DataTransformerID } from './ids';
-import { dataTransformers } from './transformers';
-import { toDataFrame } from '../processDataFrame';
-import { ReducerID } from '../fieldReducer';
-import { DataFrameView } from '../dataFrameView';
+import { DataTransformerID } from './transformers/ids';
+import { transformersRegistry } from './transformers';
+import { toDataFrame } from '../dataframe/processDataFrame';
+import { ReducerID } from './fieldReducer';
+import { DataFrameView } from '../dataframe/DataFrameView';
 
 describe('Transformers', () => {
   it('should load all transformeres', () => {
     for (const name of Object.keys(DataTransformerID)) {
-      const calc = dataTransformers.get(name);
+      const calc = transformersRegistry.get(name);
       expect(calc.id).toBe(name);
     }
   });
@@ -20,7 +20,7 @@ describe('Transformers', () => {
   });
 
   it('should use fluent API', () => {
-    const results = dataTransformers.reduce([seriesWithValues], {
+    const results = transformersRegistry.reduce([seriesWithValues], {
       reducers: [ReducerID.first],
     });
     expect(results.length).toBe(1);

+ 11 - 25
packages/grafana-data/src/utils/transformers/transformers.ts → packages/grafana-data/src/transformations/transformers.ts

@@ -1,19 +1,13 @@
-import { DataFrame } from '../../types/dataFrame';
-import { Registry, RegistryItemWithOptions } from '../registry';
-
-/**
- * Immutable data transformation
- */
-export type DataTransformer = (data: DataFrame[]) => DataFrame[];
-
-export interface DataTransformerInfo<TOptions = any> extends RegistryItemWithOptions {
-  transformer: (options: TOptions) => DataTransformer;
-}
+import { DataFrame } from '../types/dataFrame';
+import { Registry } from '../utils/Registry';
+// Initalize the Registry
 
-export interface DataTransformerConfig<TOptions = any> {
-  id: string;
-  options: TOptions;
-}
+import { appendTransformer, AppendOptions } from './transformers/append';
+import { reduceTransformer, ReduceTransformerOptions } from './transformers/reduce';
+import { filterFieldsTransformer, filterFramesTransformer } from './transformers/filter';
+import { filterFieldsByNameTransformer, FilterFieldsByNameTransformerOptions } from './transformers/filterByName';
+import { noopTransformer } from './transformers/noop';
+import { DataTransformerInfo, DataTransformerConfig } from '../types/transformations';
 
 /**
  * Apply configured transformations to the input data
@@ -21,7 +15,7 @@ export interface DataTransformerConfig<TOptions = any> {
 export function transformDataFrame(options: DataTransformerConfig[], data: DataFrame[]): DataFrame[] {
   let processed = data;
   for (const config of options) {
-    const info = dataTransformers.get(config.id);
+    const info = transformersRegistry.get(config.id);
     const transformer = info.transformer(config.options);
     const after = transformer(processed);
 
@@ -43,14 +37,6 @@ export function transformDataFrame(options: DataTransformerConfig[], data: DataF
   return processed;
 }
 
-// Initalize the Registry
-
-import { appendTransformer, AppendOptions } from './append';
-import { reduceTransformer, ReduceTransformerOptions } from './reduce';
-import { filterFieldsTransformer, filterFramesTransformer } from './filter';
-import { filterFieldsByNameTransformer, FilterFieldsByNameTransformerOptions } from './filterByName';
-import { noopTransformer } from './noop';
-
 /**
  * Registry of transformation options that can be driven by
  * stored configuration files.
@@ -73,7 +59,7 @@ class TransformerRegistry extends Registry<DataTransformerInfo> {
   }
 }
 
-export const dataTransformers = new TransformerRegistry(() => [
+export const transformersRegistry = new TransformerRegistry(() => [
   noopTransformer,
   filterFieldsTransformer,
   filterFieldsByNameTransformer,

+ 0 - 0
packages/grafana-data/src/utils/transformers/__snapshots__/reduce.test.ts.snap → packages/grafana-data/src/transformations/transformers/__snapshots__/reduce.test.ts.snap


+ 4 - 3
packages/grafana-data/src/utils/transformers/append.test.ts → packages/grafana-data/src/transformations/transformers/append.test.ts

@@ -1,6 +1,7 @@
-import { transformDataFrame, dataTransformers } from './transformers';
 import { DataTransformerID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
+import { transformDataFrame } from '../transformers';
+import { transformersRegistry } from '../transformers';
 
 const seriesAB = toDataFrame({
   columns: [{ text: 'A' }, { text: 'B' }],
@@ -24,7 +25,7 @@ describe('Append Transformer', () => {
       id: DataTransformerID.append,
       options: {},
     };
-    const x = dataTransformers.get(DataTransformerID.append);
+    const x = transformersRegistry.get(DataTransformerID.append);
     expect(x.id).toBe(cfg.id);
 
     const processed = transformDataFrame([cfg], [seriesAB, seriesBC])[0];

+ 2 - 2
packages/grafana-data/src/utils/transformers/append.ts → packages/grafana-data/src/transformations/transformers/append.ts

@@ -1,7 +1,7 @@
-import { DataTransformerInfo } from './transformers';
 import { DataFrame } from '../../types/dataFrame';
 import { DataTransformerID } from './ids';
-import { MutableDataFrame } from '../dataFrameHelper';
+import { MutableDataFrame } from '../../dataframe/MutableDataFrame';
+import { DataTransformerInfo } from '../../types/transformations';
 
 export interface AppendOptions {}
 

+ 3 - 3
packages/grafana-data/src/utils/transformers/filter.test.ts → packages/grafana-data/src/transformations/transformers/filter.test.ts

@@ -1,8 +1,8 @@
 import { FieldType } from '../../types/dataFrame';
-import { FieldMatcherID } from '../matchers/ids';
-import { transformDataFrame } from './transformers';
 import { DataTransformerID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
+import { FieldMatcherID } from '../matchers/ids';
+import { transformDataFrame } from '../transformers';
 
 export const simpleSeriesWithTypes = toDataFrame({
   fields: [

+ 3 - 3
packages/grafana-data/src/utils/transformers/filter.ts → packages/grafana-data/src/transformations/transformers/filter.ts

@@ -1,9 +1,9 @@
-import { DataTransformerInfo } from './transformers';
 import { noopTransformer } from './noop';
 import { DataFrame, Field } from '../../types/dataFrame';
-import { FieldMatcherID } from '../matchers/ids';
 import { DataTransformerID } from './ids';
-import { MatcherConfig, getFieldMatcher, getFrameMatchers } from '../matchers/matchers';
+import { DataTransformerInfo, MatcherConfig } from '../../types/transformations';
+import { FieldMatcherID } from '../matchers/ids';
+import { getFieldMatcher, getFrameMatchers } from '../matchers';
 
 export interface FilterOptions {
   include?: MatcherConfig;

+ 3 - 2
packages/grafana-data/src/utils/transformers/filterByName.test.ts → packages/grafana-data/src/transformations/transformers/filterByName.test.ts

@@ -1,6 +1,7 @@
-import { toDataFrame, transformDataFrame } from '../index';
-import { FieldType } from '../../index';
 import { DataTransformerID } from './ids';
+import { transformDataFrame } from '../transformers';
+import { toDataFrame } from '../../dataframe/processDataFrame';
+import { FieldType } from '../../types/dataFrame';
 
 export const seriesWithNamesToMatch = toDataFrame({
   fields: [

+ 2 - 2
packages/grafana-data/src/utils/transformers/filterByName.ts → packages/grafana-data/src/transformations/transformers/filterByName.ts

@@ -1,7 +1,7 @@
-import { DataTransformerInfo } from './transformers';
-import { FieldMatcherID } from '../matchers/ids';
 import { DataTransformerID } from './ids';
 import { filterFieldsTransformer, FilterOptions } from './filter';
+import { DataTransformerInfo } from '../../types/transformations';
+import { FieldMatcherID } from '../matchers/ids';
 
 export interface FilterFieldsByNameTransformerOptions {
   include?: string;

+ 0 - 0
packages/grafana-data/src/utils/transformers/ids.ts → packages/grafana-data/src/transformations/transformers/ids.ts


+ 1 - 1
packages/grafana-data/src/utils/transformers/noop.ts → packages/grafana-data/src/transformations/transformers/noop.ts

@@ -1,6 +1,6 @@
-import { DataTransformerInfo } from './transformers';
 import { DataTransformerID } from './ids';
 import { DataFrame } from '../../types/dataFrame';
+import { DataTransformerInfo } from '../../types/transformations';
 
 export interface NoopTransformerOptions {
   include?: string;

+ 2 - 2
packages/grafana-data/src/utils/transformers/reduce.test.ts → packages/grafana-data/src/transformations/transformers/reduce.test.ts

@@ -1,7 +1,7 @@
-import { transformDataFrame } from './transformers';
 import { ReducerID } from '../fieldReducer';
 import { DataTransformerID } from './ids';
-import { toDataFrame, toDataFrameDTO } from '../processDataFrame';
+import { toDataFrame, toDataFrameDTO } from '../../dataframe/processDataFrame';
+import { transformDataFrame } from '../transformers';
 
 const seriesWithValues = toDataFrame({
   fields: [

+ 6 - 6
packages/grafana-data/src/utils/transformers/reduce.ts → packages/grafana-data/src/transformations/transformers/reduce.ts

@@ -1,12 +1,12 @@
-import { DataTransformerInfo } from './transformers';
-import { DataFrame, FieldType, Field } from '../../types/dataFrame';
-import { MatcherConfig, getFieldMatcher } from '../matchers/matchers';
-import { alwaysFieldMatcher } from '../matchers/predicates';
 import { DataTransformerID } from './ids';
+import { MatcherConfig, DataTransformerInfo } from '../../types/transformations';
 import { ReducerID, fieldReducers, reduceField } from '../fieldReducer';
+import { alwaysFieldMatcher } from '../matchers/predicates';
+import { DataFrame, Field, FieldType } from '../../types/dataFrame';
+import { ArrayVector } from '../../vector/ArrayVector';
 import { KeyValue } from '../../types/data';
-import { ArrayVector } from '../vector';
-import { guessFieldTypeForField } from '../processDataFrame';
+import { guessFieldTypeForField } from '../../dataframe/processDataFrame';
+import { getFieldMatcher } from '../matchers';
 
 export interface ReduceTransformerOptions {
   reducers: ReducerID[];

+ 2 - 20
packages/grafana-data/src/types/dataFrame.ts

@@ -1,9 +1,10 @@
 import { Threshold } from './threshold';
 import { ValueMapping } from './valueMapping';
 import { QueryResultBase, Labels, NullValueMode } from './data';
-import { FieldCalcs } from '../utils/index';
 import { DisplayProcessor } from './displayValue';
 import { DataLink } from './dataLink';
+import { Vector } from './vector';
+import { FieldCalcs } from '../transformations/fieldReducer';
 
 export enum FieldType {
   time = 'time', // or date
@@ -44,25 +45,6 @@ export interface FieldConfig {
   noValue?: string;
 }
 
-export interface Vector<T = any> {
-  length: number;
-
-  /**
-   * Access the value by index (Like an array)
-   */
-  get(index: number): T;
-
-  /**
-   * Get the resutls as an array.
-   */
-  toArray(): T[];
-
-  /**
-   * Return the values as a simple array for json serialization
-   */
-  toJSON(): any; // same results as toArray()
-}
-
 export interface Field<T = any, V = Vector<T>> {
   name: string; // The column name
   type: FieldType;

+ 1 - 0
packages/grafana-data/src/types/index.ts

@@ -11,3 +11,4 @@ export * from './valueMapping';
 export * from './displayValue';
 export * from './graph';
 export * from './ScopedVars';
+export * from './transformations';

+ 1 - 1
packages/grafana-data/src/types/time.ts

@@ -1,4 +1,4 @@
-import { DateTime } from '../utils/moment_wrapper';
+import { DateTime } from '../datetime/moment_wrapper';
 
 export interface RawTimeRange {
   from: DateTime | string;

+ 32 - 0
packages/grafana-data/src/types/transformations.ts

@@ -0,0 +1,32 @@
+import { DataFrame, Field } from './dataFrame';
+import { RegistryItemWithOptions } from '../utils/Registry';
+
+/**
+ * Immutable data transformation
+ */
+export type DataTransformer = (data: DataFrame[]) => DataFrame[];
+
+export interface DataTransformerInfo<TOptions = any> extends RegistryItemWithOptions {
+  transformer: (options: TOptions) => DataTransformer;
+}
+
+export interface DataTransformerConfig<TOptions = any> {
+  id: string;
+  options: TOptions;
+}
+
+export type FieldMatcher = (field: Field) => boolean;
+export type FrameMatcher = (frame: DataFrame) => boolean;
+
+export interface FieldMatcherInfo<TOptions = any> extends RegistryItemWithOptions<TOptions> {
+  get: (options: TOptions) => FieldMatcher;
+}
+
+export interface FrameMatcherInfo<TOptions = any> extends RegistryItemWithOptions<TOptions> {
+  get: (options: TOptions) => FrameMatcher;
+}
+
+export interface MatcherConfig<TOptions = any> {
+  id: string;
+  options?: TOptions;
+}

+ 40 - 0
packages/grafana-data/src/types/vector.ts

@@ -0,0 +1,40 @@
+export interface Vector<T = any> {
+  length: number;
+
+  /**
+   * Access the value by index (Like an array)
+   */
+  get(index: number): T;
+
+  /**
+   * Get the resutls as an array.
+   */
+  toArray(): T[];
+
+  /**
+   * Return the values as a simple array for json serialization
+   */
+  toJSON(): any; // same results as toArray()
+}
+
+/**
+ * Apache arrow vectors are Read/Write
+ */
+export interface ReadWriteVector<T = any> extends Vector<T> {
+  set: (index: number, value: T) => void;
+}
+
+/**
+ * Vector with standard manipulation functions
+ */
+export interface MutableVector<T = any> extends ReadWriteVector<T> {
+  /**
+   * Adds the value to the vector
+   */
+  add: (value: T) => void;
+
+  /**
+   * modifies the vector so it is now the oposite order
+   */
+  reverse: () => void;
+}

+ 0 - 0
packages/grafana-data/src/utils/registry.ts → packages/grafana-data/src/utils/Registry.ts


+ 2 - 2
packages/grafana-data/src/utils/csv.test.ts

@@ -1,9 +1,9 @@
 import { readCSV, toCSV, CSVHeaderStyle } from './csv';
-import { getDataFrameRow } from './processDataFrame';
+import { getDataFrameRow } from '../dataframe/processDataFrame';
 
 // Test with local CSV files
 import fs from 'fs';
-import { toDataFrameDTO } from './processDataFrame';
+import { toDataFrameDTO } from '../dataframe/processDataFrame';
 
 describe('read csv', () => {
   it('should get X and y', () => {

+ 2 - 2
packages/grafana-data/src/utils/csv.ts

@@ -5,8 +5,8 @@ import isNumber from 'lodash/isNumber';
 
 // Types
 import { DataFrame, Field, FieldType, FieldConfig } from '../types';
-import { guessFieldTypeFromValue } from './processDataFrame';
-import { MutableDataFrame } from './dataFrameHelper';
+import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
+import { MutableDataFrame } from '../dataframe/MutableDataFrame';
 
 export enum CSVHeaderStyle {
   full,

+ 28 - 0
packages/grafana-data/src/utils/fieldParser.ts

@@ -0,0 +1,28 @@
+import { Field, FieldType } from '../types/dataFrame';
+import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
+
+export function makeFieldParser(value: any, field: Field): (value: string) => any {
+  if (!field.type) {
+    if (field.name === 'time' || field.name === 'Time') {
+      field.type = FieldType.time;
+    } else {
+      field.type = guessFieldTypeFromValue(value);
+    }
+  }
+
+  if (field.type === FieldType.number) {
+    return (value: string) => {
+      return parseFloat(value);
+    };
+  }
+
+  // Will convert anything that starts with "T" to true
+  if (field.type === FieldType.boolean) {
+    return (value: string) => {
+      return !(value[0] === 'F' || value[0] === 'f' || value[0] === '0');
+    };
+  }
+
+  // Just pass the string back
+  return (value: string) => value;
+}

+ 1 - 20
packages/grafana-data/src/utils/index.ts

@@ -1,29 +1,10 @@
-export * from './string';
-export * from './registry';
-export * from './markdown';
-export * from './processDataFrame';
+export * from './Registry';
 export * from './deprecationWarning';
 export * from './csv';
-export * from './fieldReducer';
 export * from './logs';
 export * from './labels';
 export * from './labels';
 export * from './object';
-export * from './moment_wrapper';
 export * from './thresholds';
-export * from './text';
-export * from './dataFrameHelper';
-export * from './dataFrameView';
-export * from './vector';
 
 export { getMappedValue } from './valueMappings';
-
-// Names are too general to export globally
-import * as dateMath from './datemath';
-import * as rangeUtil from './rangeutil';
-export { dateMath, rangeUtil };
-
-export * from './matchers/ids';
-export * from './matchers/matchers';
-export * from './transformers/ids';
-export * from './transformers/transformers';

+ 1 - 1
packages/grafana-data/src/utils/logs.ts

@@ -2,7 +2,7 @@ import { countBy, chain, map, escapeRegExp } from 'lodash';
 
 import { LogLevel, LogRowModel, LogLabelStatsModel, LogsParser } from '../types/logs';
 import { DataFrame, FieldType } from '../types/index';
-import { ArrayVector } from './vector';
+import { ArrayVector } from '../vector/ArrayVector';
 
 const LOGFMT_REGEXP = /(?:^|\s)(\w+)=("[^"]*"|\S+)/;
 

+ 0 - 338
packages/grafana-data/src/utils/vector.ts

@@ -1,338 +0,0 @@
-import { Vector } from '../types/dataFrame';
-
-export function vectorToArray<T>(v: Vector<T>): T[] {
-  const arr: T[] = [];
-  for (let i = 0; i < v.length; i++) {
-    arr[i] = v.get(i);
-  }
-  return arr;
-}
-
-/**
- * Apache arrow vectors are Read/Write
- */
-export interface ReadWriteVector<T = any> extends Vector<T> {
-  set: (index: number, value: T) => void;
-}
-
-/**
- * Vector with standard manipulation functions
- */
-export interface MutableVector<T = any> extends ReadWriteVector<T> {
-  /**
-   * Adds the value to the vector
-   */
-  add: (value: T) => void;
-
-  /**
-   * modifies the vector so it is now the oposite order
-   */
-  reverse: () => void;
-}
-
-export class ArrayVector<T = any> implements MutableVector<T> {
-  buffer: T[];
-
-  constructor(buffer?: T[]) {
-    this.buffer = buffer ? buffer : [];
-  }
-
-  get length() {
-    return this.buffer.length;
-  }
-
-  add(value: T) {
-    this.buffer.push(value);
-  }
-
-  get(index: number): T {
-    return this.buffer[index];
-  }
-
-  set(index: number, value: T) {
-    this.buffer[index] = value;
-  }
-
-  reverse() {
-    this.buffer.reverse();
-  }
-
-  toArray(): T[] {
-    return this.buffer;
-  }
-
-  toJSON(): T[] {
-    return this.buffer;
-  }
-}
-
-export class ConstantVector<T = any> implements Vector<T> {
-  constructor(private value: T, private len: number) {}
-
-  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();
-  }
-}
-
-export class ScaledVector implements Vector<number> {
-  constructor(private source: Vector<number>, private scale: number) {}
-
-  get length(): number {
-    return this.source.length;
-  }
-
-  get(index: number): number {
-    return this.source.get(index) * this.scale;
-  }
-
-  toArray(): number[] {
-    return vectorToArray(this);
-  }
-
-  toJSON(): number[] {
-    return vectorToArray(this);
-  }
-}
-
-/**
- * Values are returned in the order defined by the input parameter
- */
-export class SortedVector<T = any> implements Vector<T> {
-  constructor(private source: Vector<T>, private order: number[]) {}
-
-  get length(): number {
-    return this.source.length;
-  }
-
-  get(index: number): T {
-    return this.source.get(this.order[index]);
-  }
-
-  toArray(): T[] {
-    return vectorToArray(this);
-  }
-
-  toJSON(): T[] {
-    return vectorToArray(this);
-  }
-}
-
-interface CircularOptions<T> {
-  buffer?: T[];
-  append?: 'head' | 'tail';
-  capacity?: number;
-}
-
-/**
- * Circular vector uses a single buffer to capture a stream of values
- * overwriting the oldest value on add.
- *
- * This supports addting to the 'head' or 'tail' and will grow the buffer
- * to match a configured capacity.
- */
-export class CircularVector<T = any> implements MutableVector<T> {
-  private buffer: T[];
-  private index: number;
-  private capacity: number;
-  private tail: boolean;
-
-  constructor(options: CircularOptions<T>) {
-    this.buffer = options.buffer || [];
-    this.capacity = this.buffer.length;
-    this.tail = 'head' !== options.append;
-    this.index = 0;
-
-    this.add = this.getAddFunction();
-    if (options.capacity) {
-      this.setCapacity(options.capacity);
-    }
-  }
-
-  /**
-   * This gets the appropriate add function depending on the buffer state:
-   *  * head vs tail
-   *  * growing buffer vs overwriting values
-   */
-  private getAddFunction() {
-    // When we are not at capacity, it should actually modify the buffer
-    if (this.capacity > this.buffer.length) {
-      if (this.tail) {
-        return (value: T) => {
-          this.buffer.push(value);
-          if (this.buffer.length >= this.capacity) {
-            this.add = this.getAddFunction();
-          }
-        };
-      } else {
-        return (value: T) => {
-          this.buffer.unshift(value);
-          if (this.buffer.length >= this.capacity) {
-            this.add = this.getAddFunction();
-          }
-        };
-      }
-    }
-
-    if (this.tail) {
-      return (value: T) => {
-        this.buffer[this.index] = value;
-        this.index = (this.index + 1) % this.buffer.length;
-      };
-    }
-
-    // Append values to the head
-    return (value: T) => {
-      let idx = this.index - 1;
-      if (idx < 0) {
-        idx = this.buffer.length - 1;
-      }
-      this.buffer[idx] = value;
-      this.index = idx;
-    };
-  }
-
-  setCapacity(v: number) {
-    if (this.capacity === v) {
-      return;
-    }
-    // Make a copy so it is in order and new additions can be at the head or tail
-    const copy = this.toArray();
-    if (v > this.length) {
-      this.buffer = copy;
-    } else if (v < this.capacity) {
-      // Shrink the buffer
-      const delta = this.length - v;
-      if (this.tail) {
-        this.buffer = copy.slice(delta, copy.length); // Keep last items
-      } else {
-        this.buffer = copy.slice(0, copy.length - delta); // Keep first items
-      }
-    }
-    this.capacity = v;
-    this.index = 0;
-    this.add = this.getAddFunction();
-  }
-
-  setAppendMode(mode: 'head' | 'tail') {
-    const tail = 'head' !== mode;
-    if (tail !== this.tail) {
-      this.buffer = this.toArray().reverse();
-      this.index = 0;
-      this.tail = tail;
-      this.add = this.getAddFunction();
-    }
-  }
-
-  reverse() {
-    this.buffer.reverse();
-  }
-
-  /**
-   * Add the value to the buffer
-   */
-  add: (value: T) => void;
-
-  get(index: number) {
-    return this.buffer[(index + this.index) % this.buffer.length];
-  }
-
-  set(index: number, value: T) {
-    this.buffer[(index + this.index) % this.buffer.length] = value;
-  }
-
-  get length() {
-    return this.buffer.length;
-  }
-
-  toArray(): T[] {
-    return vectorToArray(this);
-  }
-
-  toJSON(): T[] {
-    return vectorToArray(this);
-  }
-}
-
-interface AppendedVectorInfo<T> {
-  start: number;
-  end: number;
-  values: Vector<T>;
-}
-
-/**
- * This may be more trouble than it is worth.  This trades some computation time for
- * RAM -- rather than allocate a new array the size of all previous arrays, this just
- * points the correct index to their original array values
- */
-export class AppendedVectors<T = any> implements Vector<T> {
-  length = 0;
-  source: Array<AppendedVectorInfo<T>> = new Array<AppendedVectorInfo<T>>();
-
-  constructor(startAt = 0) {
-    this.length = startAt;
-  }
-
-  /**
-   * Make the vector look like it is this long
-   */
-  setLength(length: number) {
-    if (length > this.length) {
-      // make the vector longer (filling with undefined)
-      this.length = length;
-    } else if (length < this.length) {
-      // make the array shorter
-      const sources: Array<AppendedVectorInfo<T>> = new Array<AppendedVectorInfo<T>>();
-      for (const src of this.source) {
-        sources.push(src);
-        if (src.end > length) {
-          src.end = length;
-          break;
-        }
-      }
-      this.source = sources;
-      this.length = length;
-    }
-  }
-
-  append(v: Vector<T>): AppendedVectorInfo<T> {
-    const info = {
-      start: this.length,
-      end: this.length + v.length,
-      values: v,
-    };
-    this.length = info.end;
-    this.source.push(info);
-    return info;
-  }
-
-  get(index: number): T {
-    for (let i = 0; i < this.source.length; i++) {
-      const src = this.source[i];
-      if (index >= src.start && index < src.end) {
-        return src.values.get(index - src.start);
-      }
-    }
-    return (undefined as unknown) as T;
-  }
-
-  toArray(): T[] {
-    return vectorToArray(this);
-  }
-
-  toJSON(): T[] {
-    return vectorToArray(this);
-  }
-}

+ 23 - 0
packages/grafana-data/src/vector/AppendedVectors.test.ts

@@ -0,0 +1,23 @@
+import { ArrayVector } from './ArrayVector';
+import { AppendedVectors } from './AppendedVectors';
+
+describe('Check Appending Vector', () => {
+  it('should transparently join them', () => {
+    const appended = new AppendedVectors();
+    appended.append(new ArrayVector([1, 2, 3]));
+    appended.append(new ArrayVector([4, 5, 6]));
+    appended.append(new ArrayVector([7, 8, 9]));
+    expect(appended.length).toEqual(9);
+
+    appended.setLength(5);
+    expect(appended.length).toEqual(5);
+    appended.append(new ArrayVector(['a', 'b', 'c']));
+    expect(appended.length).toEqual(8);
+    expect(appended.toArray()).toEqual([1, 2, 3, 4, 5, 'a', 'b', 'c']);
+
+    appended.setLength(2);
+    appended.setLength(6);
+    appended.append(new ArrayVector(['x', 'y', 'z']));
+    expect(appended.toArray()).toEqual([1, 2, undefined, undefined, undefined, undefined, 'x', 'y', 'z']);
+  });
+});

+ 73 - 0
packages/grafana-data/src/vector/AppendedVectors.ts

@@ -0,0 +1,73 @@
+import { Vector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+interface AppendedVectorInfo<T> {
+  start: number;
+  end: number;
+  values: Vector<T>;
+}
+
+/**
+ * This may be more trouble than it is worth.  This trades some computation time for
+ * RAM -- rather than allocate a new array the size of all previous arrays, this just
+ * points the correct index to their original array values
+ */
+export class AppendedVectors<T = any> implements Vector<T> {
+  length = 0;
+  source: Array<AppendedVectorInfo<T>> = new Array<AppendedVectorInfo<T>>();
+
+  constructor(startAt = 0) {
+    this.length = startAt;
+  }
+
+  /**
+   * Make the vector look like it is this long
+   */
+  setLength(length: number) {
+    if (length > this.length) {
+      // make the vector longer (filling with undefined)
+      this.length = length;
+    } else if (length < this.length) {
+      // make the array shorter
+      const sources: Array<AppendedVectorInfo<T>> = new Array<AppendedVectorInfo<T>>();
+      for (const src of this.source) {
+        sources.push(src);
+        if (src.end > length) {
+          src.end = length;
+          break;
+        }
+      }
+      this.source = sources;
+      this.length = length;
+    }
+  }
+
+  append(v: Vector<T>): AppendedVectorInfo<T> {
+    const info = {
+      start: this.length,
+      end: this.length + v.length,
+      values: v,
+    };
+    this.length = info.end;
+    this.source.push(info);
+    return info;
+  }
+
+  get(index: number): T {
+    for (let i = 0; i < this.source.length; i++) {
+      const src = this.source[i];
+      if (index >= src.start && index < src.end) {
+        return src.values.get(index - src.start);
+      }
+    }
+    return (undefined as unknown) as T;
+  }
+
+  toArray(): T[] {
+    return vectorToArray(this);
+  }
+
+  toJSON(): T[] {
+    return vectorToArray(this);
+  }
+}

+ 37 - 0
packages/grafana-data/src/vector/ArrayVector.ts

@@ -0,0 +1,37 @@
+import { MutableVector } from '../types/vector';
+
+export class ArrayVector<T = any> implements MutableVector<T> {
+  buffer: T[];
+
+  constructor(buffer?: T[]) {
+    this.buffer = buffer ? buffer : [];
+  }
+
+  get length() {
+    return this.buffer.length;
+  }
+
+  add(value: T) {
+    this.buffer.push(value);
+  }
+
+  get(index: number): T {
+    return this.buffer[index];
+  }
+
+  set(index: number, value: T) {
+    this.buffer[index] = value;
+  }
+
+  reverse() {
+    this.buffer.reverse();
+  }
+
+  toArray(): T[] {
+    return this.buffer;
+  }
+
+  toJSON(): T[] {
+    return this.buffer;
+  }
+}

+ 1 - 49
packages/grafana-data/src/utils/vector.test.ts → packages/grafana-data/src/vector/CircularVector.test.ts

@@ -1,31 +1,4 @@
-import { ConstantVector, ScaledVector, ArrayVector, CircularVector, AppendedVectors } from './vector';
-
-describe('Check Proxy Vector', () => {
-  it('should support constant values', () => {
-    const value = 3.5;
-    const v = new ConstantVector(value, 7);
-    expect(v.length).toEqual(7);
-
-    expect(v.get(0)).toEqual(value);
-    expect(v.get(1)).toEqual(value);
-
-    // Now check all of them
-    for (let i = 0; i < 10; i++) {
-      expect(v.get(i)).toEqual(value);
-    }
-  });
-
-  it('should support multiply operations', () => {
-    const source = new ArrayVector([1, 2, 3, 4]);
-    const scale = 2.456;
-    const v = new ScaledVector(source, scale);
-    expect(v.length).toEqual(source.length);
-    //  expect(v.push(10)).toEqual(source.length); // not implemented
-    for (let i = 0; i < 10; i++) {
-      expect(v.get(i)).toEqual(source.get(i) * scale);
-    }
-  });
-});
+import { CircularVector } from './CircularVector';
 
 describe('Check Circular Vector', () => {
   it('should append values', () => {
@@ -156,24 +129,3 @@ describe('Check Circular Vector', () => {
     expect(v.toArray()).toEqual([3, 4, 5]);
   });
 });
-
-describe('Check Appending Vector', () => {
-  it('should transparently join them', () => {
-    const appended = new AppendedVectors();
-    appended.append(new ArrayVector([1, 2, 3]));
-    appended.append(new ArrayVector([4, 5, 6]));
-    appended.append(new ArrayVector([7, 8, 9]));
-    expect(appended.length).toEqual(9);
-
-    appended.setLength(5);
-    expect(appended.length).toEqual(5);
-    appended.append(new ArrayVector(['a', 'b', 'c']));
-    expect(appended.length).toEqual(8);
-    expect(appended.toArray()).toEqual([1, 2, 3, 4, 5, 'a', 'b', 'c']);
-
-    appended.setLength(2);
-    appended.setLength(6);
-    appended.append(new ArrayVector(['x', 'y', 'z']));
-    expect(appended.toArray()).toEqual([1, 2, undefined, undefined, undefined, undefined, 'x', 'y', 'z']);
-  });
-});

+ 138 - 0
packages/grafana-data/src/vector/CircularVector.ts

@@ -0,0 +1,138 @@
+import { MutableVector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+interface CircularOptions<T> {
+  buffer?: T[];
+  append?: 'head' | 'tail';
+  capacity?: number;
+}
+
+/**
+ * Circular vector uses a single buffer to capture a stream of values
+ * overwriting the oldest value on add.
+ *
+ * This supports addting to the 'head' or 'tail' and will grow the buffer
+ * to match a configured capacity.
+ */
+export class CircularVector<T = any> implements MutableVector<T> {
+  private buffer: T[];
+  private index: number;
+  private capacity: number;
+  private tail: boolean;
+
+  constructor(options: CircularOptions<T>) {
+    this.buffer = options.buffer || [];
+    this.capacity = this.buffer.length;
+    this.tail = 'head' !== options.append;
+    this.index = 0;
+
+    this.add = this.getAddFunction();
+    if (options.capacity) {
+      this.setCapacity(options.capacity);
+    }
+  }
+
+  /**
+   * This gets the appropriate add function depending on the buffer state:
+   *  * head vs tail
+   *  * growing buffer vs overwriting values
+   */
+  private getAddFunction() {
+    // When we are not at capacity, it should actually modify the buffer
+    if (this.capacity > this.buffer.length) {
+      if (this.tail) {
+        return (value: T) => {
+          this.buffer.push(value);
+          if (this.buffer.length >= this.capacity) {
+            this.add = this.getAddFunction();
+          }
+        };
+      } else {
+        return (value: T) => {
+          this.buffer.unshift(value);
+          if (this.buffer.length >= this.capacity) {
+            this.add = this.getAddFunction();
+          }
+        };
+      }
+    }
+
+    if (this.tail) {
+      return (value: T) => {
+        this.buffer[this.index] = value;
+        this.index = (this.index + 1) % this.buffer.length;
+      };
+    }
+
+    // Append values to the head
+    return (value: T) => {
+      let idx = this.index - 1;
+      if (idx < 0) {
+        idx = this.buffer.length - 1;
+      }
+      this.buffer[idx] = value;
+      this.index = idx;
+    };
+  }
+
+  setCapacity(v: number) {
+    if (this.capacity === v) {
+      return;
+    }
+    // Make a copy so it is in order and new additions can be at the head or tail
+    const copy = this.toArray();
+    if (v > this.length) {
+      this.buffer = copy;
+    } else if (v < this.capacity) {
+      // Shrink the buffer
+      const delta = this.length - v;
+      if (this.tail) {
+        this.buffer = copy.slice(delta, copy.length); // Keep last items
+      } else {
+        this.buffer = copy.slice(0, copy.length - delta); // Keep first items
+      }
+    }
+    this.capacity = v;
+    this.index = 0;
+    this.add = this.getAddFunction();
+  }
+
+  setAppendMode(mode: 'head' | 'tail') {
+    const tail = 'head' !== mode;
+    if (tail !== this.tail) {
+      this.buffer = this.toArray().reverse();
+      this.index = 0;
+      this.tail = tail;
+      this.add = this.getAddFunction();
+    }
+  }
+
+  reverse() {
+    this.buffer.reverse();
+  }
+
+  /**
+   * Add the value to the buffer
+   */
+  add: (value: T) => void;
+
+  get(index: number) {
+    return this.buffer[(index + this.index) % this.buffer.length];
+  }
+
+  set(index: number, value: T) {
+    this.buffer[(index + this.index) % this.buffer.length] = value;
+  }
+
+  get length() {
+    return this.buffer.length;
+  }
+
+  toArray(): T[] {
+    return vectorToArray(this);
+  }
+
+  toJSON(): T[] {
+    return vectorToArray(this);
+  }
+}

+ 17 - 0
packages/grafana-data/src/vector/ConstantVector.test.ts

@@ -0,0 +1,17 @@
+import { ConstantVector } from './ConstantVector';
+
+describe('ConstantVector', () => {
+  it('should support constant values', () => {
+    const value = 3.5;
+    const v = new ConstantVector(value, 7);
+    expect(v.length).toEqual(7);
+
+    expect(v.get(0)).toEqual(value);
+    expect(v.get(1)).toEqual(value);
+
+    // Now check all of them
+    for (let i = 0; i < 10; i++) {
+      expect(v.get(i)).toEqual(value);
+    }
+  });
+});

+ 22 - 0
packages/grafana-data/src/vector/ConstantVector.ts

@@ -0,0 +1,22 @@
+import { Vector } from '../types/vector';
+
+export class ConstantVector<T = any> implements Vector<T> {
+  constructor(private value: T, private len: number) {}
+
+  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();
+  }
+}

+ 15 - 0
packages/grafana-data/src/vector/ScaledVector.test.ts

@@ -0,0 +1,15 @@
+import { ArrayVector } from './ArrayVector';
+import { ScaledVector } from './ScaledVector';
+
+describe('ScaledVector', () => {
+  it('should support multiply operations', () => {
+    const source = new ArrayVector([1, 2, 3, 4]);
+    const scale = 2.456;
+    const v = new ScaledVector(source, scale);
+    expect(v.length).toEqual(source.length);
+    //  expect(v.push(10)).toEqual(source.length); // not implemented
+    for (let i = 0; i < 10; i++) {
+      expect(v.get(i)).toEqual(source.get(i) * scale);
+    }
+  });
+});

+ 22 - 0
packages/grafana-data/src/vector/ScaledVector.ts

@@ -0,0 +1,22 @@
+import { Vector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+export class ScaledVector implements Vector<number> {
+  constructor(private source: Vector<number>, private scale: number) {}
+
+  get length(): number {
+    return this.source.length;
+  }
+
+  get(index: number): number {
+    return this.source.get(index) * this.scale;
+  }
+
+  toArray(): number[] {
+    return vectorToArray(this);
+  }
+
+  toJSON(): number[] {
+    return vectorToArray(this);
+  }
+}

+ 25 - 0
packages/grafana-data/src/vector/SortedVector.ts

@@ -0,0 +1,25 @@
+import { Vector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+/**
+ * Values are returned in the order defined by the input parameter
+ */
+export class SortedVector<T = any> implements Vector<T> {
+  constructor(private source: Vector<T>, private order: number[]) {}
+
+  get length(): number {
+    return this.source.length;
+  }
+
+  get(index: number): T {
+    return this.source.get(this.order[index]);
+  }
+
+  toArray(): T[] {
+    return vectorToArray(this);
+  }
+
+  toJSON(): T[] {
+    return vectorToArray(this);
+  }
+}

+ 6 - 0
packages/grafana-data/src/vector/index.ts

@@ -0,0 +1,6 @@
+export * from './AppendedVectors';
+export * from './ArrayVector';
+export * from './CircularVector';
+export * from './ConstantVector';
+export * from './ScaledVector';
+export * from './SortedVector';

+ 9 - 0
packages/grafana-data/src/vector/vectorToArray.ts

@@ -0,0 +1,9 @@
+import { Vector } from '../types/vector';
+
+export function vectorToArray<T>(v: Vector<T>): T[] {
+  const arr: T[] = [];
+  for (let i = 0; i < v.length; i++) {
+    arr[i] = v.get(i);
+  }
+  return arr;
+}

+ 2 - 2
packages/grafana-ui/src/components/TransformersUI/FilterByNameTransformerEditor.tsx

@@ -1,5 +1,5 @@
 import React, { useContext } from 'react';
-import { FilterFieldsByNameTransformerOptions, DataTransformerID, dataTransformers, KeyValue } from '@grafana/data';
+import { FilterFieldsByNameTransformerOptions, DataTransformerID, transformersRegistry, KeyValue } from '@grafana/data';
 import { TransformerUIProps, TransformerUIRegistyItem } from './types';
 import { ThemeContext } from '../../themes/ThemeContext';
 import { css, cx } from 'emotion';
@@ -157,7 +157,7 @@ const FilterPill: React.FC<FilterPillProps> = ({ label, selected, onClick }) =>
 export const filterFieldsByNameTransformRegistryItem: TransformerUIRegistyItem<FilterFieldsByNameTransformerOptions> = {
   id: DataTransformerID.filterFieldsByName,
   component: FilterByNameTransformerEditor,
-  transformer: dataTransformers.get(DataTransformerID.filterFieldsByName),
+  transformer: transformersRegistry.get(DataTransformerID.filterFieldsByName),
   name: 'Filter by name',
   description: 'UI for filter by name transformation',
 };

+ 2 - 3
packages/grafana-ui/src/components/TransformersUI/ReduceTransformerEditor.tsx

@@ -1,8 +1,7 @@
 import React from 'react';
 import { StatsPicker } from '../StatsPicker/StatsPicker';
-import { ReduceTransformerOptions, DataTransformerID, ReducerID } from '@grafana/data';
+import { ReduceTransformerOptions, DataTransformerID, ReducerID, transformersRegistry } from '@grafana/data';
 import { TransformerUIRegistyItem, TransformerUIProps } from './types';
-import { dataTransformers } from '@grafana/data';
 
 // TODO:  Minimal implementation, needs some <3
 export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransformerOptions>> = ({
@@ -29,7 +28,7 @@ export const ReduceTransformerEditor: React.FC<TransformerUIProps<ReduceTransfor
 export const reduceTransformRegistryItem: TransformerUIRegistyItem<ReduceTransformerOptions> = {
   id: DataTransformerID.reduce,
   component: ReduceTransformerEditor,
-  transformer: dataTransformers.get(DataTransformerID.reduce),
+  transformer: transformersRegistry.get(DataTransformerID.reduce),
   name: 'Reduce',
   description: 'UI for reduce transformation',
 };

+ 1 - 1
public/app/features/explore/utils/ResultProcessor.test.ts

@@ -1,4 +1,4 @@
-jest.mock('@grafana/data/src/utils/moment_wrapper', () => ({
+jest.mock('@grafana/data/src/datetime/moment_wrapper', () => ({
   dateTime: (ts: any) => {
     return {
       valueOf: () => ts,