The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/explore/utils/ResultProcessor.ts

181 lines
5.5 KiB

import { DataQueryResponse, DataQueryResponseData } from '@grafana/ui';
import {
TableData,
isTableData,
LogsModel,
toDataFrame,
guessFieldTypes,
TimeSeries,
GraphSeriesXY,
LoadingState,
} from '@grafana/data';
import { ExploreItemState, ExploreMode } from 'app/types/explore';
import { getProcessedDataFrames } from 'app/features/dashboard/state/PanelQueryState';
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
import { sortLogsResult, refreshIntervalToSortOrder } from 'app/core/utils/explore';
import { dataFrameToLogsModel } from 'app/core/logs_model';
import { getGraphSeriesModel } from 'app/plugins/panel/graph2/getGraphSeriesModel';
export class ResultProcessor {
private rawData: DataQueryResponseData[] = [];
private metrics: TimeSeries[] = [];
private tables: TableData[] = [];
constructor(
private state: ExploreItemState,
private replacePreviousResults: boolean,
result?: DataQueryResponse | DataQueryResponseData[]
) {
if (result && result.hasOwnProperty('data')) {
this.rawData = (result as DataQueryResponse).data;
} else {
this.rawData = (result as DataQueryResponseData[]) || [];
}
if (this.state.mode !== ExploreMode.Metrics) {
return;
}
for (let index = 0; index < this.rawData.length; index++) {
const res: any = this.rawData[index];
const isTable = isTableData(res);
if (isTable) {
this.tables.push(res);
} else {
this.metrics.push(res);
}
}
}
getRawData = (): any[] => {
return this.rawData;
};
getGraphResult = (): GraphSeriesXY[] => {
if (this.state.mode !== ExploreMode.Metrics) {
return [];
}
const newResults = this.createGraphSeries(this.metrics);
return this.mergeGraphResults(newResults, this.state.graphResult);
};
getTableResult = (): TableModel => {
if (this.state.mode !== ExploreMode.Metrics) {
return new TableModel();
}
const prevTableResults: any[] | TableModel = this.state.tableResult || [];
const tablesToMerge = this.replacePreviousResults ? this.tables : [].concat(prevTableResults, this.tables);
return mergeTablesIntoModel(new TableModel(), ...tablesToMerge);
};
getLogsResult = (): LogsModel => {
if (this.state.mode !== ExploreMode.Logs) {
return null;
}
const graphInterval = this.state.queryIntervals.intervalMs;
const dataFrame = this.rawData.map(result => guessFieldTypes(toDataFrame(result)));
const newResults = this.rawData ? dataFrameToLogsModel(dataFrame, graphInterval) : null;
const sortOrder = refreshIntervalToSortOrder(this.state.refreshInterval);
const sortedNewResults = sortLogsResult(newResults, sortOrder);
if (this.replacePreviousResults) {
const slice = 1000;
const rows = sortedNewResults.rows.slice(0, slice);
const series = sortedNewResults.series;
return { ...sortedNewResults, rows, series };
}
const prevLogsResult: LogsModel = this.state.logsResult || { hasUniqueLabels: false, rows: [] };
const sortedLogResult = sortLogsResult(prevLogsResult, sortOrder);
const rowsInState = sortedLogResult.rows;
const seriesInState = sortedLogResult.series || [];
const processedRows = [];
for (const row of rowsInState) {
processedRows.push({ ...row, fresh: false });
}
for (const row of sortedNewResults.rows) {
processedRows.push({ ...row, fresh: true });
}
const processedSeries = this.mergeGraphResults(sortedNewResults.series, seriesInState);
const slice = -1000;
const rows = processedRows.slice(slice);
const series = processedSeries.slice(slice);
return { ...sortedNewResults, rows, series };
};
private createGraphSeries = (rawData: any[]) => {
const dataFrames = getProcessedDataFrames(rawData);
const graphSeries = getGraphSeriesModel(
{ series: dataFrames, state: LoadingState.Done },
{},
{ showBars: false, showLines: true, showPoints: false },
{
asTable: false,
isVisible: true,
placement: 'under',
}
);
return graphSeries;
};
private isSameGraphSeries = (a: GraphSeriesXY, b: GraphSeriesXY) => {
if (a.hasOwnProperty('label') && b.hasOwnProperty('label')) {
const aValue = a.label;
const bValue = b.label;
if (aValue !== undefined && bValue !== undefined && aValue === bValue) {
return true;
}
}
return false;
};
private mergeGraphResults = (newResults: GraphSeriesXY[], prevResults: GraphSeriesXY[]): GraphSeriesXY[] => {
if (!prevResults || prevResults.length === 0 || this.replacePreviousResults) {
return newResults; // Hack before we use GraphSeriesXY instead
}
const results: GraphSeriesXY[] = prevResults.slice() as GraphSeriesXY[];
// update existing results
for (let index = 0; index < results.length; index++) {
const prevResult = results[index];
for (const newResult of newResults) {
const isSame = this.isSameGraphSeries(prevResult, newResult);
if (isSame) {
prevResult.data = prevResult.data.concat(newResult.data);
break;
}
}
}
// add new results
for (const newResult of newResults) {
let isNew = true;
for (const prevResult of results) {
const isSame = this.isSameGraphSeries(prevResult, newResult);
if (isSame) {
isNew = false;
break;
}
}
if (isNew) {
results.push(newResult);
}
}
return results;
};
}