From c06debe20046a2e1062b632728eb8856f195bd96 Mon Sep 17 00:00:00 2001 From: Ben Tasker <88340935+btasker@users.noreply.github.com> Date: Wed, 15 Nov 2023 11:21:41 +0000 Subject: [PATCH] InfluxDB: Add support for `>=` and `<=` comparison operators to IQL Query Builder (#77917) * InfluxDB: Add support for `>=` and `<=` comparison operators to InfluxQL Query Builder * Add front-end support for the new operators This ensures that the query translates correctly between raw and builder mode * Chore: add test for new operators * chore: add front-end tests * fix: don't skip quoting on `<>` This preserves the pre-existing behaviour, fixing a failing test * chore: fix tests --- pkg/tsdb/influxdb/models/query.go | 2 +- pkg/tsdb/influxdb/models/query_test.go | 11 ++++++ .../query/influxql/visual/TagsSection.tsx | 4 +- .../influxdb/influx_query_model.test.ts | 38 +++++++++++++++++++ .../datasource/influxdb/influx_query_model.ts | 2 +- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/pkg/tsdb/influxdb/models/query.go b/pkg/tsdb/influxdb/models/query.go index 0c6155521b4..4fb0a6e863f 100644 --- a/pkg/tsdb/influxdb/models/query.go +++ b/pkg/tsdb/influxdb/models/query.go @@ -72,7 +72,7 @@ func (query *Query) renderTags() []string { switch tag.Operator { case "=~", "!~": textValue = tag.Value - case "<", ">": + case "<", ">", ">=", "<=": textValue = tag.Value default: textValue = fmt.Sprintf("'%s'", strings.ReplaceAll(tag.Value, `\`, `\\`)) diff --git a/pkg/tsdb/influxdb/models/query_test.go b/pkg/tsdb/influxdb/models/query_test.go index 8bda0359394..2cc6026c4c5 100644 --- a/pkg/tsdb/influxdb/models/query_test.go +++ b/pkg/tsdb/influxdb/models/query_test.go @@ -228,6 +228,17 @@ func TestInfluxdbQueryBuilder(t *testing.T) { require.Equal(t, strings.Join(query.renderTags(), ""), `"key" > 10001`) }) + t.Run("can render number greater than or equal to condition tags", func(t *testing.T) { + query := &Query{Tags: []*Tag{{Operator: ">=", Value: "10001", Key: "key"}}} + + require.Equal(t, strings.Join(query.renderTags(), ""), `"key" >= 10001`) + }) + t.Run("can render number less than or equal to condition tags", func(t *testing.T) { + query := &Query{Tags: []*Tag{{Operator: "<=", Value: "10001", Key: "key"}}} + + require.Equal(t, strings.Join(query.renderTags(), ""), `"key" <= 10001`) + }) + t.Run("can render string tags", func(t *testing.T) { query := &Query{Tags: []*Tag{{Operator: "=", Value: "value", Key: "key"}}} diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx index 983c2d5fb07..bcc17db289d 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/TagsSection.tsx @@ -10,8 +10,8 @@ import { toSelectableValue } from '../utils/toSelectableValue'; import { AddButton } from './AddButton'; import { Seg } from './Seg'; -type KnownOperator = '=' | '!=' | '<>' | '<' | '>' | '=~' | '!~'; -const knownOperators: KnownOperator[] = ['=', '!=', '<>', '<', '>', '=~', '!~']; +type KnownOperator = '=' | '!=' | '<>' | '<' | '>' | '>=' | '<=' | '=~' | '!~'; +const knownOperators: KnownOperator[] = ['=', '!=', '<>', '<', '>', '>=', '<=', '=~', '!~']; type KnownCondition = 'AND' | 'OR'; const knownConditions: KnownCondition[] = ['AND', 'OR']; diff --git a/public/app/plugins/datasource/influxdb/influx_query_model.test.ts b/public/app/plugins/datasource/influxdb/influx_query_model.test.ts index cd0bf8f878b..b88cf7eeef6 100644 --- a/public/app/plugins/datasource/influxdb/influx_query_model.test.ts +++ b/public/app/plugins/datasource/influxdb/influx_query_model.test.ts @@ -206,6 +206,44 @@ describe('InfluxQuery', () => { }); }); + describe('query with greater-than-or-equal-to condition', () => { + it('should use >=', () => { + const query = new InfluxQueryModel( + { + refId: 'A', + measurement: 'cpu', + policy: 'autogen', + groupBy: [], + tags: [{ key: 'value', value: '5', operator: '>=' }], + }, + templateSrv, + {} + ); + + const queryText = query.render(); + expect(queryText).toBe('SELECT mean("value") FROM "autogen"."cpu" WHERE ("value" >= 5) AND $timeFilter'); + }); + }); + + describe('query with less-than-or-equal-to condition', () => { + it('should use <=', () => { + const query = new InfluxQueryModel( + { + refId: 'A', + measurement: 'cpu', + policy: 'autogen', + groupBy: [], + tags: [{ key: 'value', value: '5', operator: '<=' }], + }, + templateSrv, + {} + ); + + const queryText = query.render(); + expect(queryText).toBe('SELECT mean("value") FROM "autogen"."cpu" WHERE ("value" <= 5) AND $timeFilter'); + }); + }); + describe('series with groupByTag', () => { it('should generate correct query', () => { const query = new InfluxQueryModel( diff --git a/public/app/plugins/datasource/influxdb/influx_query_model.ts b/public/app/plugins/datasource/influxdb/influx_query_model.ts index 3356e2d40ed..337ef905f76 100644 --- a/public/app/plugins/datasource/influxdb/influx_query_model.ts +++ b/public/app/plugins/datasource/influxdb/influx_query_model.ts @@ -163,7 +163,7 @@ export default class InfluxQueryModel { if (interpolate) { value = this.templateSrv.replace(value, this.scopedVars); } - if (operator !== '>' && operator !== '<') { + if ((!operator.startsWith('>') && !operator.startsWith('<')) || operator === '<>') { value = "'" + value.replace(/\\/g, '\\\\').replace(/\'/g, "\\'") + "'"; } } else if (interpolate) {