mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(elasticsearch): a lot of work to support aggregation queries without date_histogram, queries that return metric aggregations now work with the table panel (json data type), #3219
This commit is contained in:
@@ -27,7 +27,7 @@ export class TablePanelCtrl {
|
|||||||
var panelDefaults = {
|
var panelDefaults = {
|
||||||
targets: [{}],
|
targets: [{}],
|
||||||
transform: 'timeseries_to_rows',
|
transform: 'timeseries_to_rows',
|
||||||
pageSize: 50,
|
pageSize: null,
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
styles: [
|
styles: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
Pagination (Page size)
|
Pagination (Page size)
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input type="number" class="input-small tight-form-input" placeholder="50"
|
<input type="number" class="input-small tight-form-input" placeholder="100"
|
||||||
empty-to-null ng-model="panel.pageSize" ng-change="render()" ng-model-onblur>
|
empty-to-null ng-model="panel.pageSize" ng-change="render()" ng-model-onblur>
|
||||||
</li>
|
</li>
|
||||||
<li class="tight-form-item">
|
<li class="tight-form-item">
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function tablePanelEditor() {
|
|||||||
|
|
||||||
scope.updateColumnsMenu = function(data) {
|
scope.updateColumnsMenu = function(data) {
|
||||||
scope.columnsMenu = transformers[scope.panel.transform].getColumns(data);
|
scope.columnsMenu = transformers[scope.panel.transform].getColumns(data);
|
||||||
scope.showColumnOptions = scope.columnsMenu.length > 0;
|
scope.showColumnOptions = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.$on('render', function(event, table, rawData) {
|
scope.$on('render', function(event, table, rawData) {
|
||||||
@@ -51,6 +51,7 @@ export function tablePanelEditor() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
scope.transformChanged = function() {
|
scope.transformChanged = function() {
|
||||||
|
scope.panel.columns = [];
|
||||||
scope.updateColumnsMenu();
|
scope.updateColumnsMenu();
|
||||||
scope.render();
|
scope.render();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ export function tablePanel() {
|
|||||||
function appendPaginationControls(footerElem) {
|
function appendPaginationControls(footerElem) {
|
||||||
footerElem.empty();
|
footerElem.empty();
|
||||||
|
|
||||||
var pageCount = Math.ceil(data.rows.length / panel.pageSize);
|
var pageSize = panel.pageSize || 100;
|
||||||
|
var pageCount = Math.ceil(data.rows.length / pageSize);
|
||||||
if (pageCount === 1) {
|
if (pageCount === 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,18 +56,12 @@ export function tablePanel() {
|
|||||||
|
|
||||||
var paginationList = $('<ul></ul>');
|
var paginationList = $('<ul></ul>');
|
||||||
|
|
||||||
// var prevLink = $('<li><a class="table-panel-page-link pointer">«</a></li>');
|
|
||||||
// paginationList.append(prevLink);
|
|
||||||
|
|
||||||
for (var i = startPage; i < endPage; i++) {
|
for (var i = startPage; i < endPage; i++) {
|
||||||
var activeClass = i === scope.pageIndex ? 'active' : '';
|
var activeClass = i === scope.pageIndex ? 'active' : '';
|
||||||
var pageLinkElem = $('<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i+1) + '</a></li>');
|
var pageLinkElem = $('<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i+1) + '</a></li>');
|
||||||
paginationList.append(pageLinkElem);
|
paginationList.append(pageLinkElem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// var nextLink = $('<li><a href="#">»</a></li>');
|
|
||||||
// paginationList.append(nextLink);
|
|
||||||
|
|
||||||
footerElem.append(paginationList);
|
footerElem.append(paginationList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,8 +119,9 @@ export class TableRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(page) {
|
render(page) {
|
||||||
let startPos = page * this.panel.pageSize;
|
let pageSize = this.panel.pageSize || 100;
|
||||||
let endPos = Math.min(startPos + this.panel.pageSize, this.table.rows.length);
|
let startPos = page * pageSize;
|
||||||
|
let endPos = Math.min(startPos + pageSize, this.table.rows.length);
|
||||||
var html = "";
|
var html = "";
|
||||||
|
|
||||||
for (var y = startPos; y < endPos; y++) {
|
for (var y = startPos; y < endPos; y++) {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ transformers['json'] = {
|
|||||||
var names: any = {};
|
var names: any = {};
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
var series = data[i];
|
var series = data[i];
|
||||||
if (series.type === 'docs') {
|
if (series.type !== 'docs') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,20 +86,71 @@ function (_, queryDef) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ElasticResponse.prototype.processAggregationDocs = function(esAgg, aggDef, target, docs, props) {
|
||||||
|
var metric, y, i, bucket, metricName, doc;
|
||||||
|
|
||||||
|
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||||
|
bucket = esAgg.buckets[i];
|
||||||
|
doc = _.defaults({}, props);
|
||||||
|
doc[aggDef.field] = bucket.key;
|
||||||
|
|
||||||
|
for (y = 0; y < target.metrics.length; y++) {
|
||||||
|
metric = target.metrics[y];
|
||||||
|
|
||||||
|
switch(metric.type) {
|
||||||
|
case "count": {
|
||||||
|
metricName = this._getMetricName(metric.type);
|
||||||
|
doc[metricName] = bucket.doc_count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'extended_stats': {
|
||||||
|
for (var statName in metric.meta) {
|
||||||
|
if (!metric.meta[statName]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stats = bucket[metric.id];
|
||||||
|
// add stats that are in nested obj to top level obj
|
||||||
|
stats.std_deviation_bounds_upper = stats.std_deviation_bounds.upper;
|
||||||
|
stats.std_deviation_bounds_lower = stats.std_deviation_bounds.lower;
|
||||||
|
|
||||||
|
metricName = this._getMetricName(statName);
|
||||||
|
doc[metricName] = stats[statName];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
metricName = this._getMetricName(metric.type);
|
||||||
|
doc[metricName] =bucket[metric.id].value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
docs.push(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// This is quite complex
|
// This is quite complex
|
||||||
// neeed to recurise down the nested buckets to build series
|
// neeed to recurise down the nested buckets to build series
|
||||||
ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, props) {
|
ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, docs, props, depth) {
|
||||||
var bucket, aggDef, esAgg, aggId;
|
var bucket, aggDef, esAgg, aggId;
|
||||||
|
var maxDepth = target.bucketAggs.length-1;
|
||||||
|
|
||||||
for (aggId in aggs) {
|
for (aggId in aggs) {
|
||||||
aggDef = _.findWhere(target.bucketAggs, {id: aggId});
|
aggDef = _.findWhere(target.bucketAggs, {id: aggId});
|
||||||
esAgg = aggs[aggId];
|
esAgg = aggs[aggId];
|
||||||
|
|
||||||
if (!aggDef) {
|
if (!aggDef) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aggDef.type === 'date_histogram') {
|
if (depth === maxDepth) {
|
||||||
this.processMetrics(esAgg, target, seriesList, props);
|
if (aggDef.type === 'date_histogram') {
|
||||||
|
this.processMetrics(esAgg, target, seriesList, props);
|
||||||
|
} else {
|
||||||
|
this.processAggregationDocs(esAgg, aggDef, target, docs, props);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var nameIndex in esAgg.buckets) {
|
for (var nameIndex in esAgg.buckets) {
|
||||||
bucket = esAgg.buckets[nameIndex];
|
bucket = esAgg.buckets[nameIndex];
|
||||||
@@ -109,7 +160,7 @@ function (_, queryDef) {
|
|||||||
} else {
|
} else {
|
||||||
props["filter"] = nameIndex;
|
props["filter"] = nameIndex;
|
||||||
}
|
}
|
||||||
this.processBuckets(bucket, target, seriesList, props);
|
this.processBuckets(bucket, target, seriesList, docs, props, depth+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,13 +268,18 @@ function (_, queryDef) {
|
|||||||
var aggregations = response.aggregations;
|
var aggregations = response.aggregations;
|
||||||
var target = this.targets[i];
|
var target = this.targets[i];
|
||||||
var tmpSeriesList = [];
|
var tmpSeriesList = [];
|
||||||
|
var docs = [];
|
||||||
|
|
||||||
this.processBuckets(aggregations, target, tmpSeriesList, {});
|
this.processBuckets(aggregations, target, tmpSeriesList, docs, {}, 0);
|
||||||
this.nameSeries(tmpSeriesList, target);
|
this.nameSeries(tmpSeriesList, target);
|
||||||
|
|
||||||
for (var y = 0; y < tmpSeriesList.length; y++) {
|
for (var y = 0; y < tmpSeriesList.length; y++) {
|
||||||
seriesList.push(tmpSeriesList[y]);
|
seriesList.push(tmpSeriesList[y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seriesList.length === 0 && docs.length > 0) {
|
||||||
|
seriesList.push({target: 'docs', type: 'docs', datapoints: docs});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -411,6 +411,51 @@ describe('ElasticResponse', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('No group by time', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
targets = [{
|
||||||
|
refId: 'A',
|
||||||
|
metrics: [{type: 'avg', id: '1'}, {type: 'count' }],
|
||||||
|
bucketAggs: [{id: '2', type: 'terms', field: 'host'}],
|
||||||
|
}];
|
||||||
|
|
||||||
|
response = {
|
||||||
|
responses: [{
|
||||||
|
aggregations: {
|
||||||
|
"2": {
|
||||||
|
buckets: [
|
||||||
|
{
|
||||||
|
"1": { value: 1000},
|
||||||
|
key: "server-1",
|
||||||
|
doc_count: 369,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"1": { value: 2000},
|
||||||
|
key: "server-2",
|
||||||
|
doc_count: 200,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.only('should return table', function() {
|
||||||
|
expect(result.data.length).to.be(1);
|
||||||
|
expect(result.data[0].type).to.be('docs');
|
||||||
|
expect(result.data[0].datapoints.length).to.be(2);
|
||||||
|
expect(result.data[0].datapoints[0].host).to.be("server-1");
|
||||||
|
expect(result.data[0].datapoints[0].Average).to.be(1000);
|
||||||
|
expect(result.data[0].datapoints[0].Count).to.be(369);
|
||||||
|
|
||||||
|
expect(result.data[0].datapoints[1].host).to.be("server-2");
|
||||||
|
expect(result.data[0].datapoints[1].Average).to.be(2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Raw documents query', function() {
|
describe('Raw documents query', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
targets = [{ refId: 'A', metrics: [{type: 'raw_document', id: '1'}], bucketAggs: [] }];
|
targets = [{ refId: 'A', metrics: [{type: 'raw_document', id: '1'}], bucketAggs: [] }];
|
||||||
|
|||||||
Reference in New Issue
Block a user