extractFields.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { isString } from 'lodash';
  2. import { map } from 'rxjs/operators';
  3. import {
  4. ArrayVector,
  5. DataFrame,
  6. DataTransformerID,
  7. Field,
  8. FieldType,
  9. getFieldTypeFromValue,
  10. SynchronousDataTransformerInfo,
  11. } from '@grafana/data';
  12. import { findField } from 'app/features/dimensions';
  13. import { FieldExtractorID, fieldExtractors } from './fieldExtractors';
  14. export interface ExtractFieldsOptions {
  15. source?: string;
  16. format?: FieldExtractorID;
  17. replace?: boolean;
  18. }
  19. export const extractFieldsTransformer: SynchronousDataTransformerInfo<ExtractFieldsOptions> = {
  20. id: DataTransformerID.extractFields,
  21. name: 'Extract fields',
  22. description: 'Parse fields from the contends of another',
  23. defaultOptions: {},
  24. operator: (options) => (source) => source.pipe(map((data) => extractFieldsTransformer.transformer(options)(data))),
  25. transformer: (options: ExtractFieldsOptions) => {
  26. return (data: DataFrame[]) => {
  27. return data.map((v) => addExtractedFields(v, options));
  28. };
  29. },
  30. };
  31. function addExtractedFields(frame: DataFrame, options: ExtractFieldsOptions): DataFrame {
  32. if (!options.source) {
  33. return frame;
  34. }
  35. const source = findField(frame, options.source);
  36. if (!source) {
  37. // this case can happen when there are multiple queries
  38. return frame;
  39. }
  40. const ext = fieldExtractors.getIfExists(options.format ?? FieldExtractorID.Auto);
  41. if (!ext) {
  42. throw new Error('unkonwn extractor');
  43. }
  44. const count = frame.length;
  45. const names: string[] = []; // keep order
  46. const values = new Map<string, any[]>();
  47. for (let i = 0; i < count; i++) {
  48. let obj = source.values.get(i);
  49. if (isString(obj)) {
  50. try {
  51. obj = ext.parse(obj);
  52. } catch {
  53. obj = {}; // empty
  54. }
  55. }
  56. for (const [key, val] of Object.entries(obj)) {
  57. let buffer = values.get(key);
  58. if (buffer == null) {
  59. buffer = new Array(count);
  60. values.set(key, buffer);
  61. names.push(key);
  62. }
  63. buffer[i] = val;
  64. }
  65. }
  66. const fields = names.map((name) => {
  67. const buffer = values.get(name);
  68. return {
  69. name,
  70. values: new ArrayVector(buffer),
  71. type: buffer ? getFieldTypeFromValue(buffer.find((v) => v != null)) : FieldType.other,
  72. config: {},
  73. } as Field;
  74. });
  75. if (!options.replace) {
  76. fields.unshift(...frame.fields);
  77. }
  78. return {
  79. ...frame,
  80. fields,
  81. };
  82. }