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
This commit is contained in:
Ben Tasker
2023-11-15 11:21:41 +00:00
committed by GitHub
parent 4ebfce8b9a
commit c06debe200
5 changed files with 53 additions and 4 deletions

View File

@@ -72,7 +72,7 @@ func (query *Query) renderTags() []string {
switch tag.Operator { switch tag.Operator {
case "=~", "!~": case "=~", "!~":
textValue = tag.Value textValue = tag.Value
case "<", ">": case "<", ">", ">=", "<=":
textValue = tag.Value textValue = tag.Value
default: default:
textValue = fmt.Sprintf("'%s'", strings.ReplaceAll(tag.Value, `\`, `\\`)) textValue = fmt.Sprintf("'%s'", strings.ReplaceAll(tag.Value, `\`, `\\`))

View File

@@ -228,6 +228,17 @@ func TestInfluxdbQueryBuilder(t *testing.T) {
require.Equal(t, strings.Join(query.renderTags(), ""), `"key" > 10001`) 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) { t.Run("can render string tags", func(t *testing.T) {
query := &Query{Tags: []*Tag{{Operator: "=", Value: "value", Key: "key"}}} query := &Query{Tags: []*Tag{{Operator: "=", Value: "value", Key: "key"}}}

View File

@@ -10,8 +10,8 @@ import { toSelectableValue } from '../utils/toSelectableValue';
import { AddButton } from './AddButton'; import { AddButton } from './AddButton';
import { Seg } from './Seg'; import { Seg } from './Seg';
type KnownOperator = '=' | '!=' | '<>' | '<' | '>' | '=~' | '!~'; type KnownOperator = '=' | '!=' | '<>' | '<' | '>' | '>=' | '<=' | '=~' | '!~';
const knownOperators: KnownOperator[] = ['=', '!=', '<>', '<', '>', '=~', '!~']; const knownOperators: KnownOperator[] = ['=', '!=', '<>', '<', '>', '>=', '<=', '=~', '!~'];
type KnownCondition = 'AND' | 'OR'; type KnownCondition = 'AND' | 'OR';
const knownConditions: KnownCondition[] = ['AND', 'OR']; const knownConditions: KnownCondition[] = ['AND', 'OR'];

View File

@@ -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', () => { describe('series with groupByTag', () => {
it('should generate correct query', () => { it('should generate correct query', () => {
const query = new InfluxQueryModel( const query = new InfluxQueryModel(

View File

@@ -163,7 +163,7 @@ export default class InfluxQueryModel {
if (interpolate) { if (interpolate) {
value = this.templateSrv.replace(value, this.scopedVars); value = this.templateSrv.replace(value, this.scopedVars);
} }
if (operator !== '>' && operator !== '<') { if ((!operator.startsWith('>') && !operator.startsWith('<')) || operator === '<>') {
value = "'" + value.replace(/\\/g, '\\\\').replace(/\'/g, "\\'") + "'"; value = "'" + value.replace(/\\/g, '\\\\').replace(/\'/g, "\\'") + "'";
} }
} else if (interpolate) { } else if (interpolate) {