mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Use SeriesData format for loki/logs (#16793)
This is the first step moving towards Explore supporting logs for more datasources than Loki. In the first step we move all the log processing from Loki into Explore. - Make explore convert logs result returned from datasources to SeriesData, if needed, and for now convert SeriesData to LogsModel. - Loki datasource query now returns SeriesData and all processing have been moved into explore instead. - Removed key from LogRowModel and use log row indexes as the unique key instead. - Removed id from LogsModel since it looks like it's not in use. - Introduced a new class FieldCache which is convenient to use when looking up multiple fields and/or field types and series values. Closes #16287
This commit is contained in:
committed by
GitHub
parent
26bd76b4c2
commit
fe20dde5db
@@ -19,6 +19,12 @@ export interface QueryResultMeta {
|
||||
|
||||
// Match the result to the query
|
||||
requestId?: string;
|
||||
|
||||
// Used in Explore for highlighting
|
||||
search?: string;
|
||||
|
||||
// Used in Explore to show limit applied to search result
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface QueryResultBase {
|
||||
|
||||
71
packages/grafana-ui/src/utils/fieldCache.test.ts
Normal file
71
packages/grafana-ui/src/utils/fieldCache.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { FieldType } from '../types/index';
|
||||
import { FieldCache } from './fieldCache';
|
||||
|
||||
describe('FieldCache', () => {
|
||||
it('when creating a new FieldCache from fields should be able to query cache', () => {
|
||||
const fields = [
|
||||
{ name: 'time', type: FieldType.time },
|
||||
{ name: 'string', type: FieldType.string },
|
||||
{ name: 'number', type: FieldType.number },
|
||||
{ name: 'boolean', type: FieldType.boolean },
|
||||
{ name: 'other', type: FieldType.other },
|
||||
{ name: 'undefined' },
|
||||
];
|
||||
const fieldCache = new FieldCache(fields);
|
||||
const allFields = fieldCache.getFields();
|
||||
expect(allFields).toHaveLength(6);
|
||||
|
||||
const expectedFields = [
|
||||
{ ...fields[0], index: 0 },
|
||||
{ ...fields[1], index: 1 },
|
||||
{ ...fields[2], index: 2 },
|
||||
{ ...fields[3], index: 3 },
|
||||
{ ...fields[4], index: 4 },
|
||||
{ ...fields[5], type: FieldType.other, index: 5 },
|
||||
];
|
||||
|
||||
expect(allFields).toMatchObject(expectedFields);
|
||||
|
||||
expect(fieldCache.hasFieldOfType(FieldType.time)).toBeTruthy();
|
||||
expect(fieldCache.hasFieldOfType(FieldType.string)).toBeTruthy();
|
||||
expect(fieldCache.hasFieldOfType(FieldType.number)).toBeTruthy();
|
||||
expect(fieldCache.hasFieldOfType(FieldType.boolean)).toBeTruthy();
|
||||
expect(fieldCache.hasFieldOfType(FieldType.other)).toBeTruthy();
|
||||
|
||||
expect(fieldCache.getFields(FieldType.time)).toMatchObject([expectedFields[0]]);
|
||||
expect(fieldCache.getFields(FieldType.string)).toMatchObject([expectedFields[1]]);
|
||||
expect(fieldCache.getFields(FieldType.number)).toMatchObject([expectedFields[2]]);
|
||||
expect(fieldCache.getFields(FieldType.boolean)).toMatchObject([expectedFields[3]]);
|
||||
expect(fieldCache.getFields(FieldType.other)).toMatchObject([expectedFields[4], expectedFields[5]]);
|
||||
|
||||
expect(fieldCache.getFieldByIndex(0)).toMatchObject(expectedFields[0]);
|
||||
expect(fieldCache.getFieldByIndex(1)).toMatchObject(expectedFields[1]);
|
||||
expect(fieldCache.getFieldByIndex(2)).toMatchObject(expectedFields[2]);
|
||||
expect(fieldCache.getFieldByIndex(3)).toMatchObject(expectedFields[3]);
|
||||
expect(fieldCache.getFieldByIndex(4)).toMatchObject(expectedFields[4]);
|
||||
expect(fieldCache.getFieldByIndex(5)).toMatchObject(expectedFields[5]);
|
||||
expect(fieldCache.getFieldByIndex(6)).toBeNull();
|
||||
|
||||
expect(fieldCache.getFirstFieldOfType(FieldType.time)).toMatchObject(expectedFields[0]);
|
||||
expect(fieldCache.getFirstFieldOfType(FieldType.string)).toMatchObject(expectedFields[1]);
|
||||
expect(fieldCache.getFirstFieldOfType(FieldType.number)).toMatchObject(expectedFields[2]);
|
||||
expect(fieldCache.getFirstFieldOfType(FieldType.boolean)).toMatchObject(expectedFields[3]);
|
||||
expect(fieldCache.getFirstFieldOfType(FieldType.other)).toMatchObject(expectedFields[4]);
|
||||
|
||||
expect(fieldCache.hasFieldNamed('tim')).toBeFalsy();
|
||||
expect(fieldCache.hasFieldNamed('time')).toBeTruthy();
|
||||
expect(fieldCache.hasFieldNamed('string')).toBeTruthy();
|
||||
expect(fieldCache.hasFieldNamed('number')).toBeTruthy();
|
||||
expect(fieldCache.hasFieldNamed('boolean')).toBeTruthy();
|
||||
expect(fieldCache.hasFieldNamed('other')).toBeTruthy();
|
||||
expect(fieldCache.hasFieldNamed('undefined')).toBeTruthy();
|
||||
|
||||
expect(fieldCache.getFieldByName('time')).toMatchObject(expectedFields[0]);
|
||||
expect(fieldCache.getFieldByName('string')).toMatchObject(expectedFields[1]);
|
||||
expect(fieldCache.getFieldByName('number')).toMatchObject(expectedFields[2]);
|
||||
expect(fieldCache.getFieldByName('boolean')).toMatchObject(expectedFields[3]);
|
||||
expect(fieldCache.getFieldByName('other')).toMatchObject(expectedFields[4]);
|
||||
expect(fieldCache.getFieldByName('undefined')).toMatchObject(expectedFields[5]);
|
||||
expect(fieldCache.getFieldByName('null')).toBeNull();
|
||||
});
|
||||
});
|
||||
76
packages/grafana-ui/src/utils/fieldCache.ts
Normal file
76
packages/grafana-ui/src/utils/fieldCache.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Field, FieldType } from '../types/index';
|
||||
|
||||
export interface IndexedField extends Field {
|
||||
index: number;
|
||||
}
|
||||
|
||||
export class FieldCache {
|
||||
private fields: Field[];
|
||||
private fieldIndexByName: { [key: string]: number };
|
||||
private fieldIndexByType: { [key: string]: number[] };
|
||||
|
||||
constructor(fields?: Field[]) {
|
||||
this.fields = [];
|
||||
this.fieldIndexByName = {};
|
||||
this.fieldIndexByType = {};
|
||||
this.fieldIndexByType[FieldType.time] = [];
|
||||
this.fieldIndexByType[FieldType.string] = [];
|
||||
this.fieldIndexByType[FieldType.number] = [];
|
||||
this.fieldIndexByType[FieldType.boolean] = [];
|
||||
this.fieldIndexByType[FieldType.other] = [];
|
||||
|
||||
if (fields) {
|
||||
for (let n = 0; n < fields.length; n++) {
|
||||
const field = fields[n];
|
||||
this.addField(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addField(field: Field) {
|
||||
this.fields.push({
|
||||
type: FieldType.other,
|
||||
...field,
|
||||
});
|
||||
const index = this.fields.length - 1;
|
||||
this.fieldIndexByName[field.name] = index;
|
||||
this.fieldIndexByType[field.type || FieldType.other].push(index);
|
||||
}
|
||||
|
||||
hasFieldOfType(type: FieldType): boolean {
|
||||
return this.fieldIndexByType[type] && this.fieldIndexByType[type].length > 0;
|
||||
}
|
||||
|
||||
getFields(type?: FieldType): IndexedField[] {
|
||||
const fields: IndexedField[] = [];
|
||||
for (let index = 0; index < this.fields.length; index++) {
|
||||
const field = this.fields[index];
|
||||
|
||||
if (!type || field.type === type) {
|
||||
fields.push({ ...field, index });
|
||||
}
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
getFieldByIndex(index: number): IndexedField | null {
|
||||
return this.fields[index] ? { ...this.fields[index], index } : null;
|
||||
}
|
||||
|
||||
getFirstFieldOfType(type: FieldType): IndexedField | null {
|
||||
return this.hasFieldOfType(type)
|
||||
? { ...this.fields[this.fieldIndexByType[type][0]], index: this.fieldIndexByType[type][0] }
|
||||
: null;
|
||||
}
|
||||
|
||||
hasFieldNamed(name: string): boolean {
|
||||
return this.fieldIndexByName[name] !== undefined;
|
||||
}
|
||||
|
||||
getFieldByName(name: string): IndexedField | null {
|
||||
return this.hasFieldNamed(name)
|
||||
? { ...this.fields[this.fieldIndexByName[name]], index: this.fieldIndexByName[name] }
|
||||
: null;
|
||||
}
|
||||
}
|
||||
@@ -14,3 +14,4 @@ export { getMappedValue } from './valueMappings';
|
||||
export * from './validate';
|
||||
export { getFlotPairs } from './flotPairs';
|
||||
export * from './object';
|
||||
export * from './fieldCache';
|
||||
|
||||
@@ -43,16 +43,6 @@ function convertTimeSeriesToSeriesData(timeSeries: TimeSeries): SeriesData {
|
||||
};
|
||||
}
|
||||
|
||||
export const getFirstTimeField = (series: SeriesData): number => {
|
||||
const { fields } = series;
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
if (fields[i].type === FieldType.time) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
// PapaParse Dynamic Typing regex:
|
||||
// https://github.com/mholt/PapaParse/blob/master/papaparse.js#L998
|
||||
const NUMBER = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i;
|
||||
|
||||
Reference in New Issue
Block a user