From be961c5466af2df7fd1142c5c5f1cd469625048b Mon Sep 17 00:00:00 2001 From: Marcelo Nunes Alves <35287700+MarceloNunesAlves@users.noreply.github.com> Date: Thu, 9 Jul 2020 10:21:19 -0300 Subject: [PATCH] Elasticsearch: Fix using multiple bucket script aggregations when only grouping by terms (#24064) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * In the parser when it was configurateĀ more bucket it put the formula in name column * Tests * Tests * Tests * Tests * Tests * Tests * Process/Tests - backend (Go) * Update pkg/tsdb/elasticsearch/response_parser.go Co-authored-by: Marcus Efraimsson * reverse * Update pkg/tsdb/elasticsearch/response_parser.go Co-authored-by: Marcus Efraimsson Co-authored-by: Marcus Efraimsson --- pkg/tsdb/elasticsearch/response_parser.go | 4 + .../elasticsearch/response_parser_test.go | 74 +++++++++++++++++ .../elasticsearch/elastic_response.ts | 4 + .../specs/elastic_response.test.ts | 81 ++++++++++++++++++- 4 files changed, 162 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/elasticsearch/response_parser.go b/pkg/tsdb/elasticsearch/response_parser.go index 95e1d3f1882..a5738ab5e45 100644 --- a/pkg/tsdb/elasticsearch/response_parser.go +++ b/pkg/tsdb/elasticsearch/response_parser.go @@ -372,6 +372,10 @@ func (rp *responseParser) processAggregationDocs(esAgg *simplejson.Json, aggDef if len(otherMetrics) > 1 { metricName += " " + metric.Field + if metric.Type == "bucket_script" { + //Use the formula in the column name + metricName = metric.Settings.Get("script").MustString("") + } } addMetricValue(&values, metricName, castToNullFloat(bucket.GetPath(metric.ID, "value"))) diff --git a/pkg/tsdb/elasticsearch/response_parser_test.go b/pkg/tsdb/elasticsearch/response_parser_test.go index 06f11d489e5..e638cf799f9 100644 --- a/pkg/tsdb/elasticsearch/response_parser_test.go +++ b/pkg/tsdb/elasticsearch/response_parser_test.go @@ -865,6 +865,80 @@ func TestResponseParser(t *testing.T) { So(seriesThree.Points[1][1].Float64, ShouldEqual, 2000) }) + Convey("Terms with two bucket_script", func() { + targets := map[string]string{ + "A": `{ + "timeField": "@timestamp", + "metrics": [ + { "id": "1", "type": "sum", "field": "@value" }, + { "id": "3", "type": "max", "field": "@value" }, + { + "id": "4", + "field": "select field", + "pipelineVariables": [{ "name": "var1", "pipelineAgg": "1" }, { "name": "var2", "pipelineAgg": "3" }], + "settings": { "script": "params.var1 * params.var2" }, + "type": "bucket_script" + }, + { + "id": "5", + "field": "select field", + "pipelineVariables": [{ "name": "var1", "pipelineAgg": "1" }, { "name": "var2", "pipelineAgg": "3" }], + "settings": { "script": "params.var1 * params.var2 * 2" }, + "type": "bucket_script" + } + ], + "bucketAggs": [{ "type": "terms", "field": "@timestamp", "id": "2" }] + }`, + } + response := `{ + "responses": [ + { + "aggregations": { + "2": { + "buckets": [ + { + "1": { "value": 2 }, + "3": { "value": 3 }, + "4": { "value": 6 }, + "5": { "value": 24 }, + "doc_count": 60, + "key": 1000 + }, + { + "1": { "value": 3 }, + "3": { "value": 4 }, + "4": { "value": 12 }, + "5": { "value": 48 }, + "doc_count": 60, + "key": 2000 + } + ] + } + } + } + ] + }` + rp, err := newResponseParserForTest(targets, response) + So(err, ShouldBeNil) + result, err := rp.getTimeSeries() + So(err, ShouldBeNil) + So(result.Results, ShouldHaveLength, 1) + queryRes := result.Results["A"] + So(queryRes, ShouldNotBeNil) + So(queryRes.Tables[0].Rows, ShouldHaveLength, 2) + So(queryRes.Tables[0].Columns[1].Text, ShouldEqual, "Sum") + So(queryRes.Tables[0].Columns[2].Text, ShouldEqual, "Max") + So(queryRes.Tables[0].Columns[3].Text, ShouldEqual, "params.var1 * params.var2") + So(queryRes.Tables[0].Columns[4].Text, ShouldEqual, "params.var1 * params.var2 * 2") + So(queryRes.Tables[0].Rows[0][1].(null.Float).Float64, ShouldEqual, 2) + So(queryRes.Tables[0].Rows[0][2].(null.Float).Float64, ShouldEqual, 3) + So(queryRes.Tables[0].Rows[0][3].(null.Float).Float64, ShouldEqual, 6) + So(queryRes.Tables[0].Rows[0][4].(null.Float).Float64, ShouldEqual, 24) + So(queryRes.Tables[0].Rows[1][1].(null.Float).Float64, ShouldEqual, 3) + So(queryRes.Tables[0].Rows[1][2].(null.Float).Float64, ShouldEqual, 4) + So(queryRes.Tables[0].Rows[1][3].(null.Float).Float64, ShouldEqual, 12) + So(queryRes.Tables[0].Rows[1][4].(null.Float).Float64, ShouldEqual, 48) + }) // Convey("Raw documents query", func() { // targets := map[string]string{ // "A": `{ diff --git a/public/app/plugins/datasource/elasticsearch/elastic_response.ts b/public/app/plugins/datasource/elasticsearch/elastic_response.ts index f4e45800c14..9389777d9ec 100644 --- a/public/app/plugins/datasource/elasticsearch/elastic_response.ts +++ b/public/app/plugins/datasource/elasticsearch/elastic_response.ts @@ -175,6 +175,10 @@ export class ElasticResponse { // if more of the same metric type include field field name in property if (otherMetrics.length > 1) { metricName += ' ' + metric.field; + if (metric.type === 'bucket_script') { + //Use the formula in the column name + metricName = metric.settings.script; + } } addMetricValue(values, metricName, bucket[metric.id].value); diff --git a/public/app/plugins/datasource/elasticsearch/specs/elastic_response.test.ts b/public/app/plugins/datasource/elasticsearch/specs/elastic_response.test.ts index 577f0c1edd2..4ec0dd8c8cc 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/elastic_response.test.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/elastic_response.test.ts @@ -811,7 +811,6 @@ describe('ElasticResponse', () => { result = new ElasticResponse(targets, response).getTimeSeries(); }); - it('should return 3 series', () => { expect(result.data.length).toBe(3); expect(result.data[0].datapoints.length).toBe(2); @@ -827,6 +826,86 @@ describe('ElasticResponse', () => { }); }); + describe('terms with bucket_script and two scripts', () => { + let result: any; + + beforeEach(() => { + targets = [ + { + refId: 'A', + metrics: [ + { id: '1', type: 'sum', field: '@value' }, + { id: '3', type: 'max', field: '@value' }, + { + id: '4', + field: 'select field', + pipelineVariables: [ + { name: 'var1', pipelineAgg: '1' }, + { name: 'var2', pipelineAgg: '3' }, + ], + settings: { script: 'params.var1 * params.var2' }, + type: 'bucket_script', + }, + { + id: '5', + field: 'select field', + pipelineVariables: [ + { name: 'var1', pipelineAgg: '1' }, + { name: 'var2', pipelineAgg: '3' }, + ], + settings: { script: 'params.var1 * params.var2 * 4' }, + type: 'bucket_script', + }, + ], + bucketAggs: [{ type: 'terms', field: '@timestamp', id: '2' }], + }, + ]; + response = { + responses: [ + { + aggregations: { + '2': { + buckets: [ + { + 1: { value: 2 }, + 3: { value: 3 }, + 4: { value: 6 }, + 5: { value: 24 }, + doc_count: 60, + key: 1000, + }, + { + 1: { value: 3 }, + 3: { value: 4 }, + 4: { value: 12 }, + 5: { value: 48 }, + doc_count: 60, + key: 2000, + }, + ], + }, + }, + }, + ], + }; + + result = new ElasticResponse(targets, response).getTimeSeries(); + }); + + it('should return 2 rows with 5 columns', () => { + expect(result.data[0].columns.length).toBe(5); + expect(result.data[0].rows.length).toBe(2); + expect(result.data[0].rows[0][1]).toBe(2); + expect(result.data[0].rows[0][2]).toBe(3); + expect(result.data[0].rows[0][3]).toBe(6); + expect(result.data[0].rows[0][4]).toBe(24); + expect(result.data[0].rows[1][1]).toBe(3); + expect(result.data[0].rows[1][2]).toBe(4); + expect(result.data[0].rows[1][3]).toBe(12); + expect(result.data[0].rows[1][4]).toBe(48); + }); + }); + describe('simple logs query and count', () => { const targets: any = [ {