586 lines
24 KiB
Go
586 lines
24 KiB
Go
|
package queryBuilder
|
||
|
|
||
|
import (
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
logsV3 "go.signoz.io/signoz/pkg/query-service/app/logs/v3"
|
||
|
metricsv3 "go.signoz.io/signoz/pkg/query-service/app/metrics/v3"
|
||
|
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
||
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||
|
)
|
||
|
|
||
|
func TestBuildQueryWithMultipleQueriesAndFormula(t *testing.T) {
|
||
|
t.Run("TestBuildQueryWithFilters", func(t *testing.T) {
|
||
|
q := &v3.QueryRangeParamsV3{
|
||
|
Start: 1650991982000,
|
||
|
End: 1651078382000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "name"},
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "in"}, Value: []interface{}{"a", "b", "c"}, Operator: v3.FilterOperatorIn},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorRateMax,
|
||
|
Temporality: v3.Cumulative,
|
||
|
Expression: "A",
|
||
|
},
|
||
|
"B": {
|
||
|
QueryName: "B",
|
||
|
StepInterval: 60,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "name2"},
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
AggregateOperator: v3.AggregateOperatorRateAvg,
|
||
|
Temporality: v3.Cumulative,
|
||
|
Expression: "B",
|
||
|
},
|
||
|
"C": {
|
||
|
QueryName: "C",
|
||
|
Expression: "A/B",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
qbOptions := QueryBuilderOptions{
|
||
|
BuildMetricQuery: metricsv3.PrepareMetricQuery,
|
||
|
}
|
||
|
fm := featureManager.StartManager()
|
||
|
qb := NewQueryBuilder(qbOptions, fm)
|
||
|
|
||
|
queries, err := qb.PrepareQueries(q)
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
require.Contains(t, queries["C"], "SELECT A.`ts` as `ts`, A.value / B.value")
|
||
|
require.Contains(t, queries["C"], "WHERE metric_name = 'name' AND temporality = 'Cumulative' AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'in') IN ['a','b','c']")
|
||
|
require.Contains(t, queries["C"], "(value - lagInFrame(value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window)))")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestBuildQueryWithIncorrectQueryRef(t *testing.T) {
|
||
|
t.Run("TestBuildQueryWithFilters", func(t *testing.T) {
|
||
|
q := &v3.QueryRangeParamsV3{
|
||
|
Start: 1650991982000,
|
||
|
End: 1651078382000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "name"},
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "in"}, Value: []interface{}{"a", "b", "c"}, Operator: v3.FilterOperatorIn},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorRateMax,
|
||
|
Expression: "A",
|
||
|
},
|
||
|
"C": {
|
||
|
QueryName: "C",
|
||
|
Expression: "A*2",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
qbOptions := QueryBuilderOptions{
|
||
|
BuildMetricQuery: metricsv3.PrepareMetricQuery,
|
||
|
}
|
||
|
fm := featureManager.StartManager()
|
||
|
qb := NewQueryBuilder(qbOptions, fm)
|
||
|
|
||
|
_, err := qb.PrepareQueries(q)
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestBuildQueryWithThreeOrMoreQueriesRefAndFormula(t *testing.T) {
|
||
|
t.Run("TestBuildQueryWithFilters", func(t *testing.T) {
|
||
|
q := &v3.QueryRangeParamsV3{
|
||
|
Start: 1650991982000,
|
||
|
End: 1651078382000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "name"},
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "in"}, Value: []interface{}{"a", "b", "c"}, Operator: v3.FilterOperatorIn},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorRateMax,
|
||
|
Expression: "A",
|
||
|
Disabled: true,
|
||
|
},
|
||
|
"B": {
|
||
|
QueryName: "B",
|
||
|
StepInterval: 60,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "name2"},
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
|
||
|
AggregateOperator: v3.AggregateOperatorRateMax,
|
||
|
Expression: "B",
|
||
|
Disabled: true,
|
||
|
},
|
||
|
"C": {
|
||
|
QueryName: "C",
|
||
|
StepInterval: 60,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "name3"},
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
|
||
|
AggregateOperator: v3.AggregateOperatorSumRate,
|
||
|
Expression: "C",
|
||
|
Disabled: true,
|
||
|
},
|
||
|
"F1": {
|
||
|
QueryName: "F1",
|
||
|
Expression: "A/B",
|
||
|
},
|
||
|
"F2": {
|
||
|
QueryName: "F2",
|
||
|
Expression: "A/(B+C)",
|
||
|
},
|
||
|
"F3": {
|
||
|
QueryName: "F3",
|
||
|
Expression: "A*A",
|
||
|
},
|
||
|
"F4": {
|
||
|
QueryName: "F4",
|
||
|
Expression: "A*B*C",
|
||
|
},
|
||
|
"F5": {
|
||
|
QueryName: "F5",
|
||
|
Expression: "((A - B) / B) * 100",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
qbOptions := QueryBuilderOptions{
|
||
|
BuildMetricQuery: metricsv3.PrepareMetricQuery,
|
||
|
}
|
||
|
fm := featureManager.StartManager()
|
||
|
qb := NewQueryBuilder(qbOptions, fm)
|
||
|
|
||
|
queries, err := qb.PrepareQueries(q)
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
require.Contains(t, queries["F1"], "SELECT A.`ts` as `ts`, A.value / B.value")
|
||
|
require.Equal(t, 1, strings.Count(queries["F1"], " ON "))
|
||
|
|
||
|
require.Contains(t, queries["F2"], "SELECT A.`ts` as `ts`, A.value / (B.value + C.value)")
|
||
|
require.Equal(t, 2, strings.Count(queries["F2"], " ON "))
|
||
|
|
||
|
// Working with same query multiple times should not join on itself
|
||
|
require.NotContains(t, queries["F3"], " ON ")
|
||
|
|
||
|
require.Contains(t, queries["F4"], "SELECT A.`ts` as `ts`, A.value * B.value * C.value")
|
||
|
require.Equal(t, 2, strings.Count(queries["F4"], " ON "))
|
||
|
|
||
|
require.Contains(t, queries["F5"], "SELECT A.`ts` as `ts`, ((A.value - B.value) / B.value) * 100")
|
||
|
require.Equal(t, 1, strings.Count(queries["F5"], " ON "))
|
||
|
|
||
|
for _, query := range q.CompositeQuery.BuilderQueries {
|
||
|
if query.Disabled {
|
||
|
require.NotContains(t, queries, query.QueryName)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// res := PrepareBuilderMetricQueries(q, "table")
|
||
|
// So(res.Err, ShouldBeNil)
|
||
|
// queries := res.Queries
|
||
|
// So(len(queries), ShouldEqual, 5)
|
||
|
// So(queries["F1"], ShouldContainSubstring, "SELECT A.ts as ts, A.value / B.value")
|
||
|
// So(strings.Count(queries["F1"], " ON "), ShouldEqual, 1)
|
||
|
|
||
|
// So(queries["F2"], ShouldContainSubstring, "SELECT A.ts as ts, A.value / (B.value + C.value)")
|
||
|
// So(strings.Count(queries["F2"], " ON "), ShouldEqual, 2)
|
||
|
|
||
|
// // Working with same query multiple times should not join on itself
|
||
|
// So(queries["F3"], ShouldNotContainSubstring, " ON ")
|
||
|
|
||
|
// So(queries["F4"], ShouldContainSubstring, "SELECT A.ts as ts, A.value * B.value * C.value")
|
||
|
// // Number of times JOIN ON appears is N-1 where N is number of unique queries
|
||
|
// So(strings.Count(queries["F4"], " ON "), ShouldEqual, 2)
|
||
|
|
||
|
// So(queries["F5"], ShouldContainSubstring, "SELECT A.ts as ts, ((A.value - B.value) / B.value) * 100")
|
||
|
// So(strings.Count(queries["F5"], " ON "), ShouldEqual, 1)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestDeltaQueryBuilder(t *testing.T) {
|
||
|
cases := []struct {
|
||
|
name string
|
||
|
query *v3.QueryRangeParamsV3
|
||
|
queryToTest string
|
||
|
expected string
|
||
|
}{
|
||
|
{
|
||
|
name: "TestQueryWithName - Request rate",
|
||
|
query: &v3.QueryRangeParamsV3{
|
||
|
Start: 1650991982000,
|
||
|
End: 1651078382000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
PanelType: v3.PanelTypeGraph,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
QueryName: "A",
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "signoz_latency_count"},
|
||
|
StepInterval: 60,
|
||
|
AggregateOperator: v3.AggregateOperatorSumRate,
|
||
|
Expression: "A",
|
||
|
Temporality: v3.Delta,
|
||
|
Filters: &v3.FilterSet{
|
||
|
Items: []v3.FilterItem{
|
||
|
{
|
||
|
Key: v3.AttributeKey{Key: "service_name"},
|
||
|
Operator: v3.FilterOperatorIn,
|
||
|
Value: []interface{}{"frontend"},
|
||
|
},
|
||
|
{
|
||
|
Key: v3.AttributeKey{Key: "operation"},
|
||
|
Operator: v3.FilterOperatorIn,
|
||
|
Value: []interface{}{"HTTP GET /dispatch"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
queryToTest: "A",
|
||
|
expected: "SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_count' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_count' AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts",
|
||
|
},
|
||
|
{
|
||
|
name: "TestQueryWithExpression - Error rate",
|
||
|
query: &v3.QueryRangeParamsV3{
|
||
|
Start: 1650991982000,
|
||
|
End: 1651078382000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
PanelType: v3.PanelTypeGraph,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "signoz_latency_count"},
|
||
|
StepInterval: 60,
|
||
|
AggregateOperator: v3.AggregateOperatorSumRate,
|
||
|
Expression: "A",
|
||
|
Temporality: v3.Delta,
|
||
|
Filters: &v3.FilterSet{
|
||
|
Items: []v3.FilterItem{
|
||
|
{
|
||
|
Key: v3.AttributeKey{Key: "service_name"},
|
||
|
Operator: v3.FilterOperatorIn,
|
||
|
Value: []interface{}{"frontend"},
|
||
|
},
|
||
|
{
|
||
|
Key: v3.AttributeKey{Key: "operation"},
|
||
|
Operator: v3.FilterOperatorIn,
|
||
|
Value: []interface{}{"HTTP GET /dispatch"},
|
||
|
},
|
||
|
{
|
||
|
Key: v3.AttributeKey{Key: "status_code"},
|
||
|
Operator: v3.FilterOperatorIn,
|
||
|
Value: []interface{}{"STATUS_CODE_ERROR"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
"B": {
|
||
|
QueryName: "B",
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "signoz_latency_count"},
|
||
|
StepInterval: 60,
|
||
|
AggregateOperator: v3.AggregateOperatorSumRate,
|
||
|
Expression: "B",
|
||
|
Temporality: v3.Delta,
|
||
|
Filters: &v3.FilterSet{
|
||
|
Items: []v3.FilterItem{
|
||
|
{
|
||
|
Key: v3.AttributeKey{Key: "service_name"},
|
||
|
Operator: v3.FilterOperatorIn,
|
||
|
Value: []interface{}{"frontend"},
|
||
|
},
|
||
|
{
|
||
|
Key: v3.AttributeKey{Key: "operation"},
|
||
|
Operator: v3.FilterOperatorIn,
|
||
|
Value: []interface{}{"HTTP GET /dispatch"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
"C": {
|
||
|
QueryName: "C",
|
||
|
Expression: "A*100/B",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
queryToTest: "C",
|
||
|
expected: "SELECT A.`ts` as `ts`, A.value * 100 / B.value as value FROM (SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_count' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, 'status_code') IN ['STATUS_CODE_ERROR'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_count' AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts) as A INNER JOIN (SELECT toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_count' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_count' AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY ts ORDER BY ts) as B ON A.`ts` = B.`ts`",
|
||
|
},
|
||
|
{
|
||
|
name: "TestQuery - Quantile",
|
||
|
query: &v3.QueryRangeParamsV3{
|
||
|
Start: 1650991982000,
|
||
|
End: 1651078382000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
PanelType: v3.PanelTypeGraph,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
DataSource: v3.DataSourceMetrics,
|
||
|
AggregateAttribute: v3.AttributeKey{Key: "signoz_latency_bucket"},
|
||
|
StepInterval: 60,
|
||
|
AggregateOperator: v3.AggregateOperatorHistQuant95,
|
||
|
Expression: "A",
|
||
|
Temporality: v3.Delta,
|
||
|
GroupBy: []v3.AttributeKey{
|
||
|
{Key: "service_name"},
|
||
|
{Key: "le"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
queryToTest: "A",
|
||
|
expected: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.950) as value FROM (SELECT service_name,le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli <= 1651078380000 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, c := range cases {
|
||
|
t.Run(c.name, func(t *testing.T) {
|
||
|
qbOptions := QueryBuilderOptions{
|
||
|
BuildMetricQuery: metricsv3.PrepareMetricQuery,
|
||
|
}
|
||
|
fm := featureManager.StartManager()
|
||
|
qb := NewQueryBuilder(qbOptions, fm)
|
||
|
queries, err := qb.PrepareQueries(c.query)
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, c.expected, queries[c.queryToTest])
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var testLogsWithFormula = []struct {
|
||
|
Name string
|
||
|
Query *v3.QueryRangeParamsV3
|
||
|
ExpectedQuery string
|
||
|
}{
|
||
|
{
|
||
|
Name: "test formula without dot in filter and group by attribute",
|
||
|
Query: &v3.QueryRangeParamsV3{
|
||
|
Start: 1702979275000000000,
|
||
|
End: 1702981075000000000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
PanelType: v3.PanelTypeGraph,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceLogs,
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "key_1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag}, Value: true, Operator: v3.FilterOperatorEqual},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||
|
Expression: "A",
|
||
|
OrderBy: []v3.OrderBy{
|
||
|
{
|
||
|
ColumnName: "timestamp",
|
||
|
Order: "desc",
|
||
|
},
|
||
|
},
|
||
|
GroupBy: []v3.AttributeKey{
|
||
|
{Key: "key_1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag},
|
||
|
},
|
||
|
},
|
||
|
"B": {
|
||
|
QueryName: "B",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceLogs,
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "key_2", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag}, Value: true, Operator: v3.FilterOperatorEqual},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||
|
Expression: "B",
|
||
|
OrderBy: []v3.OrderBy{
|
||
|
{
|
||
|
ColumnName: "timestamp",
|
||
|
Order: "desc",
|
||
|
},
|
||
|
},
|
||
|
GroupBy: []v3.AttributeKey{
|
||
|
{Key: "key_1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag},
|
||
|
},
|
||
|
},
|
||
|
"C": {
|
||
|
QueryName: "C",
|
||
|
Expression: "A + B",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
ExpectedQuery: "SELECT A.`key_1` as `key_1`, A.`ts` as `ts`, A.value + B.value as value FROM " +
|
||
|
"(SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] as `key_1`, toFloat64(count(*)) as value from " +
|
||
|
"signoz_logs.distributed_logs where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] = true AND " +
|
||
|
"has(attributes_bool_key, 'key_1') group by `key_1`,ts order by value DESC) as A INNER JOIN (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, " +
|
||
|
"attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] as `key_1`, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1702979275000000000 AND timestamp <= 1702981075000000000) " +
|
||
|
"AND attributes_bool_value[indexOf(attributes_bool_key, 'key_2')] = true AND has(attributes_bool_key, 'key_1') group by `key_1`,ts order by value DESC) as B ON A.`key_1` = B.`key_1` AND A.`ts` = B.`ts`",
|
||
|
},
|
||
|
{
|
||
|
Name: "test formula with dot in filter and group by attribute",
|
||
|
Query: &v3.QueryRangeParamsV3{
|
||
|
Start: 1702979056000000000,
|
||
|
End: 1702982656000000000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
PanelType: v3.PanelTypeTable,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceLogs,
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "key1.1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag}, Value: true, Operator: v3.FilterOperatorEqual},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||
|
Expression: "A",
|
||
|
OrderBy: []v3.OrderBy{
|
||
|
{
|
||
|
ColumnName: "timestamp",
|
||
|
Order: "desc",
|
||
|
},
|
||
|
},
|
||
|
GroupBy: []v3.AttributeKey{
|
||
|
{Key: "key1.1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag},
|
||
|
},
|
||
|
},
|
||
|
"B": {
|
||
|
QueryName: "B",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceLogs,
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "key1.2", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag}, Value: true, Operator: v3.FilterOperatorEqual},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||
|
Expression: "B",
|
||
|
OrderBy: []v3.OrderBy{
|
||
|
{
|
||
|
ColumnName: "timestamp",
|
||
|
Order: "desc",
|
||
|
},
|
||
|
},
|
||
|
GroupBy: []v3.AttributeKey{
|
||
|
{Key: "key1.1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag},
|
||
|
},
|
||
|
},
|
||
|
"C": {
|
||
|
QueryName: "C",
|
||
|
Expression: "A + B",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
ExpectedQuery: "SELECT A.`key1.1` as `key1.1`, A.`ts` as `ts`, A.value + B.value as value FROM (SELECT now() as ts, attributes_bool_value[indexOf(attributes_bool_key, 'key1.1')] as `key1.1`, " +
|
||
|
"toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key1.1')] = true AND " +
|
||
|
"has(attributes_bool_key, 'key1.1') group by `key1.1` order by value DESC) as A INNER JOIN (SELECT now() as ts, attributes_bool_value[indexOf(attributes_bool_key, 'key1.1')] as `key1.1`, " +
|
||
|
"toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1702979056000000000 AND timestamp <= 1702982656000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key1.2')] = true AND " +
|
||
|
"has(attributes_bool_key, 'key1.1') group by `key1.1` order by value DESC) as B ON A.`key1.1` = B.`key1.1` AND A.`ts` = B.`ts`",
|
||
|
},
|
||
|
{
|
||
|
Name: "test formula with dot in filter and group by materialized attribute",
|
||
|
Query: &v3.QueryRangeParamsV3{
|
||
|
Start: 1702980884000000000,
|
||
|
End: 1702984484000000000,
|
||
|
CompositeQuery: &v3.CompositeQuery{
|
||
|
QueryType: v3.QueryTypeBuilder,
|
||
|
PanelType: v3.PanelTypeGraph,
|
||
|
BuilderQueries: map[string]*v3.BuilderQuery{
|
||
|
"A": {
|
||
|
QueryName: "A",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceLogs,
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "key_2", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag, IsColumn: true}, Value: true, Operator: v3.FilterOperatorEqual},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||
|
Expression: "A",
|
||
|
OrderBy: []v3.OrderBy{
|
||
|
{
|
||
|
ColumnName: "timestamp",
|
||
|
Order: "desc",
|
||
|
},
|
||
|
},
|
||
|
GroupBy: []v3.AttributeKey{
|
||
|
{Key: "key1.1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag, IsColumn: true},
|
||
|
},
|
||
|
},
|
||
|
"B": {
|
||
|
QueryName: "B",
|
||
|
StepInterval: 60,
|
||
|
DataSource: v3.DataSourceLogs,
|
||
|
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
|
||
|
{Key: v3.AttributeKey{Key: "key_1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag}, Value: true, Operator: v3.FilterOperatorEqual},
|
||
|
}},
|
||
|
AggregateOperator: v3.AggregateOperatorCount,
|
||
|
Expression: "B",
|
||
|
OrderBy: []v3.OrderBy{
|
||
|
{
|
||
|
ColumnName: "timestamp",
|
||
|
Order: "desc",
|
||
|
},
|
||
|
},
|
||
|
GroupBy: []v3.AttributeKey{
|
||
|
{Key: "key1.1", DataType: v3.AttributeKeyDataTypeBool, Type: v3.AttributeKeyTypeTag, IsColumn: true},
|
||
|
},
|
||
|
},
|
||
|
"C": {
|
||
|
QueryName: "C",
|
||
|
Expression: "A - B",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
ExpectedQuery: "SELECT A.`key1.1` as `key1.1`, A.`ts` as `ts`, A.value - B.value as value FROM (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, " +
|
||
|
"`attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1702980884000000000 AND timestamp <= 1702984484000000000) AND " +
|
||
|
"`attribute_bool_key_2` = true AND `attribute_bool_key1$$1_exists`=true group by `key1.1`,ts order by value DESC) as A INNER JOIN (SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), " +
|
||
|
"INTERVAL 60 SECOND) AS ts, `attribute_bool_key1$$1` as `key1.1`, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1702980884000000000 AND " +
|
||
|
"timestamp <= 1702984484000000000) AND attributes_bool_value[indexOf(attributes_bool_key, 'key_1')] = true AND `attribute_bool_key1$$1_exists`=true group by `key1.1`,ts order by value DESC) as B " +
|
||
|
"ON A.`key1.1` = B.`key1.1` AND A.`ts` = B.`ts`",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func TestLogsQueryWithFormula(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
qbOptions := QueryBuilderOptions{
|
||
|
BuildLogQuery: logsV3.PrepareLogsQuery,
|
||
|
}
|
||
|
fm := featureManager.StartManager()
|
||
|
qb := NewQueryBuilder(qbOptions, fm)
|
||
|
|
||
|
for _, test := range testLogsWithFormula {
|
||
|
t.Run(test.Name, func(t *testing.T) {
|
||
|
queries, err := qb.PrepareQueries(test.Query)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, test.ExpectedQuery, queries["C"])
|
||
|
})
|
||
|
}
|
||
|
|
||
|
}
|