signoz/pkg/query-service/app/logs/v4/query_builder_test.go
Nityananda Gohain 3866f89d3e
feat: support for case insensitive for contains and like queries (#6045)
* feat: support for case insensitive for contains and like queries

* fix: make index filter lowercase for like and not like
2024-09-23 12:27:14 +05:30

1108 lines
38 KiB
Go

package v4
import (
"testing"
"go.signoz.io/signoz/pkg/query-service/constants"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
)
func Test_getClickhouseKey(t *testing.T) {
type args struct {
key v3.AttributeKey
}
tests := []struct {
name string
args args
want string
}{
{
name: "attribute",
args: args{
key: v3.AttributeKey{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
},
want: "attributes_string['user_name']",
},
{
name: "resource",
args: args{
key: v3.AttributeKey{Key: "servicename", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource},
},
want: "resources_string['servicename']",
},
{
name: "selected field",
args: args{
key: v3.AttributeKey{Key: "bytes", DataType: v3.AttributeKeyDataTypeFloat64, Type: v3.AttributeKeyTypeTag, IsColumn: true},
},
want: "`attribute_number_bytes`",
},
{
name: "selected field resource",
args: args{
key: v3.AttributeKey{Key: "servicename", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource, IsColumn: true},
},
want: "`resource_string_servicename`",
},
{
name: "top level key",
args: args{
key: v3.AttributeKey{Key: "trace_id", DataType: v3.AttributeKeyDataTypeString},
},
want: "trace_id",
},
{
name: "name with -",
args: args{
key: v3.AttributeKey{Key: "service-name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag, IsColumn: true},
},
want: "`attribute_string_service-name`",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getClickhouseKey(tt.args.key); got != tt.want {
t.Errorf("getClickhouseKey() = %v, want %v", got, tt.want)
}
})
}
}
func Test_getSelectLabels(t *testing.T) {
type args struct {
aggregatorOperator v3.AggregateOperator
groupBy []v3.AttributeKey
}
tests := []struct {
name string
args args
want string
}{
{
name: "count",
args: args{
aggregatorOperator: v3.AggregateOperatorCount,
groupBy: []v3.AttributeKey{{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
},
want: " attributes_string['user_name'] as `user_name`,",
},
{
name: "multiple group by",
args: args{
aggregatorOperator: v3.AggregateOperatorCount,
groupBy: []v3.AttributeKey{
{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
{Key: "service_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource, IsColumn: true},
},
},
want: " attributes_string['user_name'] as `user_name`, `resource_string_service_name` as `service_name`,",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getSelectLabels(tt.args.aggregatorOperator, tt.args.groupBy); got != tt.want {
t.Errorf("getSelectLabels() = %v, want %v", got, tt.want)
}
})
}
}
func Test_getExistsNexistsFilter(t *testing.T) {
type args struct {
op v3.FilterOperator
item v3.FilterItem
}
tests := []struct {
name string
args args
want string
}{
{
name: "exists",
args: args{
op: v3.FilterOperatorExists,
item: v3.FilterItem{Key: v3.AttributeKey{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
},
want: "mapContains(attributes_string, 'user_name')",
},
{
name: "not exists",
args: args{
op: v3.FilterOperatorNotExists,
item: v3.FilterItem{Key: v3.AttributeKey{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
},
want: "not mapContains(attributes_string, 'user_name')",
},
{
name: "exists mat column",
args: args{
op: v3.FilterOperatorExists,
item: v3.FilterItem{Key: v3.AttributeKey{Key: "bytes", DataType: v3.AttributeKeyDataTypeFloat64, Type: v3.AttributeKeyTypeTag, IsColumn: true}},
},
want: "`attribute_number_bytes_exists`=true",
},
{
name: "exists top level column",
args: args{
op: v3.FilterOperatorExists,
item: v3.FilterItem{Key: v3.AttributeKey{Key: "trace_id", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeUnspecified}},
},
want: "trace_id != ''",
},
{
name: "exists top level column- number",
args: args{
op: v3.FilterOperatorNotExists,
item: v3.FilterItem{Key: v3.AttributeKey{Key: "severity_number", DataType: v3.AttributeKeyDataTypeArrayFloat64, Type: v3.AttributeKeyTypeUnspecified}},
},
want: "severity_number = 0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getExistsNexistsFilter(tt.args.op, tt.args.item); got != tt.want {
t.Errorf("getExistsNexistsFilter() = %v, want %v", got, tt.want)
}
})
}
}
func Test_buildAttributeFilter(t *testing.T) {
type args struct {
item v3.FilterItem
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "build attribute filter",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
Operator: v3.FilterOperatorEqual,
Value: "test",
},
},
want: "resources_string['service.name'] = 'test'",
wantErr: false,
},
{
name: "test for value search across all attributes",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "__attrs",
DataType: v3.AttributeKeyDataTypeString,
},
Operator: v3.FilterOperatorContains,
Value: "test",
},
},
want: "has(mapValues(attributes_string), 'test')",
},
{
name: "build attribute filter exists",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
Operator: v3.FilterOperatorExists,
},
},
want: "mapContains(resources_string, 'service.name')",
wantErr: false,
},
{
name: "build attribute filter regex",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
Operator: v3.FilterOperatorRegex,
Value: "^test",
},
},
want: "match(resources_string['service.name'], '^test')",
},
{
name: "build attribute filter contains",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
Operator: v3.FilterOperatorContains,
Value: "test",
},
},
want: "resources_string['service.name'] ILIKE '%test%'",
},
{
name: "build attribute filter contains- body",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body",
DataType: v3.AttributeKeyDataTypeString,
IsColumn: true,
},
Operator: v3.FilterOperatorContains,
Value: "test",
},
},
want: "lower(body) LIKE lower('%test%')",
},
{
name: "build attribute filter like",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
Operator: v3.FilterOperatorLike,
Value: "test%",
},
},
want: "resources_string['service.name'] ILIKE 'test%'",
},
{
name: "build attribute filter like-body",
args: args{
item: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body",
DataType: v3.AttributeKeyDataTypeString,
IsColumn: true,
},
Operator: v3.FilterOperatorLike,
Value: "test",
},
},
want: "lower(body) LIKE lower('test')",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := buildAttributeFilter(tt.args.item)
if (err != nil) != tt.wantErr {
t.Errorf("buildAttributeFilter() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("buildAttributeFilter() = %v, want %v", got, tt.want)
}
})
}
}
func Test_buildLogsTimeSeriesFilterQuery(t *testing.T) {
type args struct {
fs *v3.FilterSet
groupBy []v3.AttributeKey
aggregateAttribute v3.AttributeKey
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "build logs time series filter query",
args: args{
fs: &v3.FilterSet{
Items: []v3.FilterItem{
{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
Operator: v3.FilterOperatorEqual,
Value: "test",
},
{
Key: v3.AttributeKey{
Key: "method",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
Operator: v3.FilterOperatorEqual,
Value: "GET",
},
},
},
},
want: "attributes_string['service.name'] = 'test' AND mapContains(attributes_string, 'service.name') " +
"AND attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method')",
},
{
name: "build logs time series filter query with group by and aggregate attribute",
args: args{
fs: &v3.FilterSet{
Items: []v3.FilterItem{
{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
Operator: v3.FilterOperatorEqual,
Value: "test",
},
},
},
groupBy: []v3.AttributeKey{
{
Key: "user_name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
},
aggregateAttribute: v3.AttributeKey{
Key: "test",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
},
want: "attributes_string['service.name'] = 'test' AND mapContains(attributes_string, 'service.name') " +
"AND mapContains(attributes_string, 'user_name') AND mapContains(attributes_string, 'test')",
},
{
name: "build logs time series filter query with multiple group by and aggregate attribute",
args: args{
fs: &v3.FilterSet{
Items: []v3.FilterItem{
{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
Operator: v3.FilterOperatorEqual,
Value: "test",
},
},
},
groupBy: []v3.AttributeKey{
{
Key: "user_name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
{
Key: "host",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
{
Key: "method",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
IsColumn: true,
},
{
Key: "trace_id",
DataType: v3.AttributeKeyDataTypeString,
IsColumn: true,
},
},
aggregateAttribute: v3.AttributeKey{
Key: "test",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
},
want: "attributes_string['service.name'] = 'test' AND mapContains(attributes_string, 'service.name') " +
"AND mapContains(attributes_string, 'user_name') AND `attribute_string_method_exists`=true AND mapContains(attributes_string, 'test')",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := buildLogsTimeSeriesFilterQuery(tt.args.fs, tt.args.groupBy, tt.args.aggregateAttribute)
if (err != nil) != tt.wantErr {
t.Errorf("buildLogsTimeSeriesFilterQuery() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("buildLogsTimeSeriesFilterQuery() = %v, want %v", got, tt.want)
}
})
}
}
func Test_orderByAttributeKeyTags(t *testing.T) {
type args struct {
panelType v3.PanelType
items []v3.OrderBy
tags []v3.AttributeKey
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test 1",
args: args{
panelType: v3.PanelTypeGraph,
items: []v3.OrderBy{
{
ColumnName: "name",
Order: "asc",
},
{
ColumnName: constants.SigNozOrderByValue,
Order: "desc",
},
},
tags: []v3.AttributeKey{
{Key: "name"},
},
},
want: "`name` asc,value desc",
},
{
name: "Test Graph item not present in tag",
args: args{
panelType: v3.PanelTypeGraph,
items: []v3.OrderBy{
{
ColumnName: "name",
Order: "asc",
},
{
ColumnName: "bytes",
Order: "asc",
},
{
ColumnName: "method",
Order: "asc",
},
},
tags: []v3.AttributeKey{
{Key: "name"},
{Key: "bytes"},
},
},
want: "`name` asc,`bytes` asc",
},
{
name: "Test panel list",
args: args{
panelType: v3.PanelTypeList,
items: []v3.OrderBy{
{
ColumnName: "name",
Order: "asc",
},
{
ColumnName: constants.SigNozOrderByValue,
Order: "asc",
},
{
ColumnName: "bytes",
Order: "asc",
},
},
tags: []v3.AttributeKey{
{Key: "name"},
{Key: "bytes"},
},
},
want: "`name` asc,value asc,`bytes` asc",
},
{
name: "test 4",
args: args{
panelType: v3.PanelTypeList,
items: []v3.OrderBy{
{
ColumnName: "name",
Order: "asc",
},
{
ColumnName: constants.SigNozOrderByValue,
Order: "asc",
},
{
ColumnName: "response_time",
Order: "desc",
Key: "response_time",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
},
},
tags: []v3.AttributeKey{
{Key: "name"},
{Key: "value"},
},
},
want: "`name` asc,value asc,attributes_string['response_time'] desc",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := orderByAttributeKeyTags(tt.args.panelType, tt.args.items, tt.args.tags); got != tt.want {
t.Errorf("orderByAttributeKeyTags() = %v, want %v", got, tt.want)
}
})
}
}
func Test_generateAggregateClause(t *testing.T) {
type args struct {
op v3.AggregateOperator
aggKey string
step int64
preferRPM bool
timeFilter string
whereClause string
groupBy string
having string
orderBy string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "test rate",
args: args{
op: v3.AggregateOperatorRate,
aggKey: "test",
step: 60,
preferRPM: false,
timeFilter: "(timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458)",
whereClause: " AND attributes_string['service.name'] = 'test'",
groupBy: " group by `user_name`",
having: "",
orderBy: " order by `user_name` desc",
},
want: " count(test)/60.000000 as value from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
"(ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['service.name'] = 'test' " +
"group by `user_name` order by `user_name` desc",
},
{
name: "test P10 with all args",
args: args{
op: v3.AggregateOperatorRate,
aggKey: "test",
step: 60,
preferRPM: false,
timeFilter: "(timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458)",
whereClause: " AND attributes_string['service.name'] = 'test'",
groupBy: " group by `user_name`",
having: " having value > 10",
orderBy: " order by `user_name` desc",
},
want: " count(test)/60.000000 as value from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND " +
"(ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['service.name'] = 'test' group by `user_name` having value > 10 order by " +
"`user_name` desc",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := generateAggregateClause(tt.args.op, tt.args.aggKey, tt.args.step, tt.args.preferRPM, tt.args.timeFilter, tt.args.whereClause, tt.args.groupBy, tt.args.having, tt.args.orderBy)
if (err != nil) != tt.wantErr {
t.Errorf("generateAggreagteClause() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("generateAggreagteClause() = %v, want %v", got, tt.want)
}
})
}
}
func Test_buildLogsQuery(t *testing.T) {
type args struct {
panelType v3.PanelType
start int64
end int64
step int64
mq *v3.BuilderQuery
graphLimitQtype string
preferRPM bool
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "build logs query",
args: args{
panelType: v3.PanelTypeTable,
start: 1680066360726210000,
end: 1680066458000000000,
step: 1000,
mq: &v3.BuilderQuery{
AggregateOperator: v3.AggregateOperatorCount,
Filters: &v3.FilterSet{
Items: []v3.FilterItem{
{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
Operator: v3.FilterOperatorEqual,
Value: "test",
},
},
},
GroupBy: []v3.AttributeKey{
{
Key: "user_name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
},
OrderBy: []v3.OrderBy{
{
ColumnName: "user_name",
Order: "desc",
},
},
},
},
want: "SELECT attributes_string['user_name'] as `user_name`, toFloat64(count(*)) as value from signoz_logs.distributed_logs_v2 " +
"where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_string['service.name'] = 'test' AND mapContains(attributes_string, 'service.name') AND mapContains(attributes_string, 'user_name') " +
"group by `user_name` order by `user_name` desc",
},
{
name: "build logs query noop",
args: args{
panelType: v3.PanelTypeList,
start: 1680066360726210000,
end: 1680066458000000000,
step: 1000,
mq: &v3.BuilderQuery{
AggregateOperator: v3.AggregateOperatorNoOp,
Filters: &v3.FilterSet{
Items: []v3.FilterItem{
{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
Operator: v3.FilterOperatorEqual,
Value: "test",
},
},
},
OrderBy: []v3.OrderBy{
{
ColumnName: "timestamp",
Order: "desc",
},
},
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string " +
"from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_string['service.name'] = 'test' AND mapContains(attributes_string, 'service.name') order by timestamp desc",
},
{
name: "build logs query with all args",
args: args{
panelType: v3.PanelTypeGraph,
start: 1680066360726210000,
end: 1680066458000000000,
step: 60,
mq: &v3.BuilderQuery{
AggregateOperator: v3.AggregateOperatorAvg,
AggregateAttribute: v3.AttributeKey{
Key: "duration",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeFloat64,
},
Filters: &v3.FilterSet{
Items: []v3.FilterItem{
{
Key: v3.AttributeKey{
Key: "service.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
Operator: v3.FilterOperatorEqual,
Value: "test",
},
{
Key: v3.AttributeKey{
Key: "duration",
DataType: v3.AttributeKeyDataTypeFloat64,
Type: v3.AttributeKeyTypeTag,
},
Operator: v3.FilterOperatorGreaterThan,
Value: 1000,
},
},
},
GroupBy: []v3.AttributeKey{
{
Key: "host",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeResource,
},
},
OrderBy: []v3.OrderBy{
{
ColumnName: "host",
Order: "desc",
},
},
},
},
want: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, resources_string['host'] as `host`, avg(attributes_number['duration']) as value " +
"from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND attributes_number['duration'] > 1000.000000 AND mapContains(attributes_number, 'duration') AND mapContains(attributes_number, 'duration') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) " +
"AND simpleJSONExtractString(labels, 'service.name') = 'test' AND labels like '%service.name%test%' AND ( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) " +
"group by `host`,ts order by `host` desc",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := buildLogsQuery(tt.args.panelType, tt.args.start, tt.args.end, tt.args.step, tt.args.mq, tt.args.graphLimitQtype, tt.args.preferRPM)
if (err != nil) != tt.wantErr {
t.Errorf("buildLogsQuery() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("buildLogsQuery() = %v, want %v", got, tt.want)
}
})
}
}
func TestPrepareLogsQuery(t *testing.T) {
type args struct {
start int64
end int64
queryType v3.QueryType
panelType v3.PanelType
mq *v3.BuilderQuery
options v3.LogQBOptions
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "TABLE: Test count with JSON Filter Array, groupBy, orderBy",
args: args{
start: 1680066360726210000,
end: 1680066458000000000,
panelType: v3.PanelTypeTable,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorCount,
Expression: "A",
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{
Key: v3.AttributeKey{
Key: "body.requestor_list[*]",
DataType: "array(string)",
IsJSON: true,
},
Operator: "has",
Value: "index_service",
},
},
},
GroupBy: []v3.AttributeKey{
{Key: "name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
{Key: "host", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource},
},
OrderBy: []v3.OrderBy{
{ColumnName: "name", Order: "DESC"},
},
},
},
want: "SELECT attributes_string['name'] as `name`, resources_string['host'] as `host`, toFloat64(count(*)) as value from signoz_logs.distributed_logs_v2 where " +
"(timestamp >= 1680066360726210000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND lower(body) like lower('%requestor_list%') " +
"AND lower(body) like lower('%index_service%') AND has(JSONExtract(JSON_QUERY(body, '$.\"requestor_list\"[*]'), 'Array(String)'), 'index_service') AND mapContains(attributes_string, 'name') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
"( (simpleJSONHas(labels, 'host') AND labels like '%host%') ))) group by `name`,`host` order by `name` DESC",
},
{
name: "Test TS with limit- first",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeGraph,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateAttribute: v3.AttributeKey{Key: "name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
AggregateOperator: v3.AggregateOperatorCountDistinct,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "GET", Operator: "="},
{Key: v3.AttributeKey{Key: "service.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource}, Value: "app", Operator: "="},
},
},
Limit: 10,
GroupBy: []v3.AttributeKey{{Key: "user", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
},
options: v3.LogQBOptions{GraphLimitQtype: constants.FirstQueryGraphLimit, PreferRPM: true},
},
want: "SELECT `user` from (SELECT attributes_string['user'] as `user`, toFloat64(count(distinct(attributes_string['name']))) as value from signoz_logs.distributed_logs_v2 " +
"where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND attributes_string['method'] = 'GET' " +
"AND mapContains(attributes_string, 'method') AND mapContains(attributes_string, 'user') AND mapContains(attributes_string, 'name') AND (resource_fingerprint GLOBAL IN " +
"(SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND simpleJSONExtractString(labels, 'service.name') = 'app' " +
"AND labels like '%service.name%app%')) group by `user` order by value DESC) LIMIT 10",
},
{
name: "Test TS with limit- second",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeGraph,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateAttribute: v3.AttributeKey{Key: "name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag},
AggregateOperator: v3.AggregateOperatorCountDistinct,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "GET", Operator: "="},
{Key: v3.AttributeKey{Key: "service.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource}, Value: "app", Operator: "="},
},
},
GroupBy: []v3.AttributeKey{{Key: "user", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
Limit: 2,
},
options: v3.LogQBOptions{GraphLimitQtype: constants.SecondQueryGraphLimit},
},
want: "SELECT toStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 60 SECOND) AS ts, attributes_string['user'] as `user`, toFloat64(count(distinct(attributes_string['name']))) as value " +
"from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND " +
"attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND mapContains(attributes_string, 'user') AND mapContains(attributes_string, 'name') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE (seen_at_ts_bucket_start >= 1680064560) AND (seen_at_ts_bucket_start <= 1680066458) AND " +
"simpleJSONExtractString(labels, 'service.name') = 'app' AND labels like '%service.name%app%')) AND (`user`) GLOBAL IN (#LIMIT_PLACEHOLDER) group by `user`,ts order by value DESC",
},
{
name: "Live Tail Query",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeList,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorNoOp,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "GET", Operator: "="},
},
},
},
options: v3.LogQBOptions{IsLivetailQuery: true},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string " +
"from signoz_logs.distributed_logs_v2 where attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND ",
},
{
name: "Live Tail Query with resource attribute",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeList,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorNoOp,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "GET", Operator: "="},
{Key: v3.AttributeKey{Key: "service.name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeResource}, Value: "app", Operator: "contains"},
},
},
},
options: v3.LogQBOptions{IsLivetailQuery: true},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string from " +
"signoz_logs.distributed_logs_v2 where attributes_string['method'] = 'GET' AND mapContains(attributes_string, 'method') AND " +
"(resource_fingerprint GLOBAL IN (SELECT fingerprint FROM signoz_logs.distributed_logs_v2_resource WHERE simpleJSONExtractString(lower(labels), 'service.name') LIKE '%app%' AND lower(labels) like '%service.name%app%' AND ",
},
{
name: "Live Tail Query W/O filter",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeList,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorNoOp,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
},
options: v3.LogQBOptions{IsLivetailQuery: true},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string " +
"from signoz_logs.distributed_logs_v2 where ",
},
{
name: "Table query with limit",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeTable,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorCount,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
Limit: 10,
},
},
want: "SELECT toFloat64(count(*)) as value from signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) order by value DESC LIMIT 10",
},
{
name: "Test limit less than pageSize - order by ts",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeList,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorNoOp,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
OrderBy: []v3.OrderBy{{ColumnName: constants.TIMESTAMP, Order: "desc", Key: constants.TIMESTAMP, DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeUnspecified, IsColumn: true}},
Limit: 1,
Offset: 0,
PageSize: 5,
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string from " +
"signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"order by `timestamp` desc LIMIT 1",
},
{
name: "Test limit greater than pageSize - order by ts",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeList,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorNoOp,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "id", Type: v3.AttributeKeyTypeUnspecified, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Operator: v3.FilterOperatorLessThan, Value: "2TNh4vp2TpiWyLt3SzuadLJF2s4"},
}},
OrderBy: []v3.OrderBy{{ColumnName: constants.TIMESTAMP, Order: "desc", Key: constants.TIMESTAMP, DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeUnspecified, IsColumn: true}},
Limit: 100,
Offset: 10,
PageSize: 10,
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string from " +
"signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"AND id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by `timestamp` desc LIMIT 10",
},
{
name: "Test limit less than pageSize - order by custom",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeList,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorNoOp,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{}},
OrderBy: []v3.OrderBy{{ColumnName: "method", Order: "desc", Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
Limit: 1,
Offset: 0,
PageSize: 5,
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string from " +
"signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) " +
"order by attributes_string['method'] desc LIMIT 1 OFFSET 0",
},
{
name: "Test limit greater than pageSize - order by custom",
args: args{
start: 1680066360726,
end: 1680066458000,
queryType: v3.QueryTypeBuilder,
panelType: v3.PanelTypeList,
mq: &v3.BuilderQuery{
QueryName: "A",
StepInterval: 60,
AggregateOperator: v3.AggregateOperatorNoOp,
Expression: "A",
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "id", Type: v3.AttributeKeyTypeUnspecified, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Operator: v3.FilterOperatorLessThan, Value: "2TNh4vp2TpiWyLt3SzuadLJF2s4"},
}},
OrderBy: []v3.OrderBy{{ColumnName: "method", Order: "desc", Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
Limit: 100,
Offset: 50,
PageSize: 50,
},
},
want: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body, attributes_string, attributes_number, attributes_bool, resources_string from " +
"signoz_logs.distributed_logs_v2 where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND (ts_bucket_start >= 1680064560 AND ts_bucket_start <= 1680066458) AND " +
"id < '2TNh4vp2TpiWyLt3SzuadLJF2s4' order by attributes_string['method'] desc LIMIT 50 OFFSET 50",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := PrepareLogsQuery(tt.args.start, tt.args.end, tt.args.queryType, tt.args.panelType, tt.args.mq, tt.args.options)
if (err != nil) != tt.wantErr {
t.Errorf("PrepareLogsQuery() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("PrepareLogsQuery() = %v, want %v", got, tt.want)
}
})
}
}