signoz/pkg/query-service/app/logs/v3/enrich_query_test.go
Nityananda Gohain 899a6ab70a
fix: fetch only required traces fields (#8351)
* fix: fetch only required traces fields

* fix: remove only logs case in field name

* fix: add extra if condition for logs json field names

* fix: tests
2025-06-25 18:29:22 +05:30

1095 lines
29 KiB
Go

package v3
import (
"reflect"
"sort"
"testing"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
. "github.com/smartystreets/goconvey/convey"
)
var testEnrichmentRequiredData = []struct {
Name string
Params v3.QueryRangeParamsV3
EnrichmentRequired bool
}{
{
Name: "attribute enrichment not required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "test",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeInt64,
},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "attribute enrichment required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "test",
},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "filter enrichment required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "john", Operator: "="},
}},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "filter enrichment required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
}},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "filter enrichment required required json",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "body.xyz", IsJSON: true, DataType: v3.AttributeKeyDataTypeString}, Value: "john", Operator: "="},
}},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "groupBy enrichment required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
GroupBy: []v3.AttributeKey{{Key: "userid", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "groupBy enrichment required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
GroupBy: []v3.AttributeKey{{Key: "userid"}},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "orderBy enrichment not required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
GroupBy: []v3.AttributeKey{{Key: "userid", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
OrderBy: []v3.OrderBy{{ColumnName: "userid"}},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "orderBy enrichment required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
OrderBy: []v3.OrderBy{{ColumnName: "userid"}},
},
},
},
},
EnrichmentRequired: true,
},
{
Name: "top level key",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
GroupBy: []v3.AttributeKey{{Key: "trace_id", Type: v3.AttributeKeyTypeUnspecified, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}},
},
},
},
},
EnrichmentRequired: false,
},
{
Name: "orderBy enrichment required",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
// here we have to fallback to trace_id attribute instead of column
GroupBy: []v3.AttributeKey{{Key: "trace_id", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}},
OrderBy: []v3.OrderBy{{ColumnName: "#SIGNOZ_VALUE", Order: "ASC"}},
},
},
},
},
EnrichmentRequired: true,
},
}
func TestEnrichmentRquired(t *testing.T) {
for _, tt := range testEnrichmentRequiredData {
Convey("testEnrichmentRequiredData", t, func() {
res := EnrichmentRequired(&tt.Params)
So(res, ShouldEqual, tt.EnrichmentRequired)
})
}
}
var testEnrichParamsData = []struct {
Name string
Params v3.QueryRangeParamsV3
Fields map[string]v3.AttributeKey
Result v3.QueryRangeParamsV3
}{
{
Name: "Enriching query range v3 params",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "test",
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
}},
GroupBy: []v3.AttributeKey{{Key: "trace_id"}},
OrderBy: []v3.OrderBy{{ColumnName: "response_time"}},
},
},
},
},
Fields: map[string]v3.AttributeKey{
"test##tag##int64": {
Key: "test",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeInt64,
},
"user_name##tag##string": {
Key: "user_name",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
},
"response_time##tag##int64": {
Key: "response_time",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeInt64,
IsColumn: true,
},
},
Result: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "test",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeInt64,
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}, Value: "john", Operator: "="},
}},
GroupBy: []v3.AttributeKey{{Key: "trace_id", Type: v3.AttributeKeyTypeUnspecified, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}},
OrderBy: []v3.OrderBy{{ColumnName: "response_time", Key: "response_time", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeInt64, IsColumn: true}},
},
},
},
},
},
{
Name: "Enriching query range v3 params with dot support",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "method.name",
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "service.name"}, Value: "test", Operator: "="},
}},
GroupBy: []v3.AttributeKey{{Key: "host.name"}},
OrderBy: []v3.OrderBy{{ColumnName: "host.name"}},
},
},
},
},
Fields: map[string]v3.AttributeKey{
"method.name##tag##string": {
Key: "method.name",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
IsColumn: true,
},
"service.name##tag##string": {
Key: "service.name",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
},
"host.name##tag##string": {
Key: "host.name",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
},
},
Result: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "method.name",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
IsColumn: true,
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "service.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}, Value: "test", Operator: "="},
}},
GroupBy: []v3.AttributeKey{{Key: "host.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}},
OrderBy: []v3.OrderBy{{ColumnName: "host.name", Key: "host.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString}},
},
},
},
},
},
{
Name: "Don't enrich if other keys are non empty and not same",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "test",
Type: v3.AttributeKeyTypeResource,
DataType: v3.AttributeKeyDataTypeInt64,
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "test", Type: v3.AttributeKeyTypeTag}, Value: "test", Operator: "="},
{Key: v3.AttributeKey{Key: "test", DataType: v3.AttributeKeyDataTypeString}, Value: "test1", Operator: "="},
}},
},
},
},
},
Fields: map[string]v3.AttributeKey{
"test##tag##string": {
Key: "test",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
IsColumn: true,
},
},
Result: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "test",
Type: v3.AttributeKeyTypeResource,
DataType: v3.AttributeKeyDataTypeInt64,
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "test", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Value: "test", Operator: "="},
{Key: v3.AttributeKey{Key: "test", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Value: "test1", Operator: "="},
}},
},
},
},
},
},
{
Name: "Enrich if an attribute/resource attribute is materialized/dematerialized",
Params: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "mat_resource",
Type: v3.AttributeKeyTypeResource,
DataType: v3.AttributeKeyDataTypeInt64,
IsColumn: true,
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "mat_attr", Type: v3.AttributeKeyTypeTag, IsColumn: true}, Value: "test", Operator: "="},
{Key: v3.AttributeKey{Key: "normal_attr", DataType: v3.AttributeKeyDataTypeString, IsColumn: false}, Value: "test1", Operator: "="},
}},
},
},
},
},
Fields: map[string]v3.AttributeKey{
"mat_resource##resource##int64": {
Key: "mat_resource",
Type: v3.AttributeKeyTypeResource,
DataType: v3.AttributeKeyDataTypeInt64,
IsColumn: false,
},
"mat_attr##tag##string": {
Key: "mat_attr",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
IsColumn: false,
},
"normal_attr##tag##string": {
Key: "normal_attr",
Type: v3.AttributeKeyTypeTag,
DataType: v3.AttributeKeyDataTypeString,
IsColumn: true,
},
},
Result: v3.QueryRangeParamsV3{
CompositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "mat_resource",
Type: v3.AttributeKeyTypeResource,
DataType: v3.AttributeKeyDataTypeInt64,
IsColumn: false,
},
Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "mat_attr", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: false}, Value: "test", Operator: "="},
{Key: v3.AttributeKey{Key: "normal_attr", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Value: "test1", Operator: "="},
}},
},
},
},
},
},
}
func TestEnrichParams(t *testing.T) {
for _, tt := range testEnrichParamsData {
Convey("testEnrichmentRequiredData", t, func() {
Enrich(&tt.Params, tt.Fields)
So(tt.Params, ShouldResemble, tt.Result)
})
}
}
var testJSONFilterEnrichData = []struct {
Name string
Filter v3.FilterItem
Result v3.FilterItem
}{
{
Name: "array string",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.requestor_list[*]",
DataType: v3.AttributeKeyDataTypeUnspecified,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "has",
Value: "index_service",
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.requestor_list[*]",
DataType: v3.AttributeKeyDataTypeArrayString,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: true,
},
Operator: "has",
Value: "index_service",
},
},
{
Name: "int64",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.intx",
DataType: v3.AttributeKeyDataTypeUnspecified,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "=",
Value: 10,
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.intx",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: true,
},
Operator: "=",
Value: 10,
},
},
{
Name: "int64 string",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.intx",
DataType: v3.AttributeKeyDataTypeUnspecified,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "=",
Value: "0",
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.intx",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: true,
},
Operator: "=",
Value: int64(0),
},
},
{
Name: "float64",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.float64[*]",
DataType: v3.AttributeKeyDataTypeArrayFloat64,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "!=",
Value: 10.0,
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.float64[*]",
DataType: v3.AttributeKeyDataTypeArrayFloat64,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: true,
},
Operator: "!=",
Value: 10.0,
},
},
{
Name: "float64x",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.float64x",
DataType: v3.AttributeKeyDataTypeUnspecified,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "!=",
Value: "10.0",
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.float64x",
DataType: v3.AttributeKeyDataTypeFloat64,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: true,
},
Operator: "!=",
Value: 10.0,
},
},
{
Name: "check IN",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.attr",
DataType: v3.AttributeKeyDataTypeUnspecified,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "IN",
Value: []interface{}{"hello", "world"},
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.attr",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: true,
},
Operator: "IN",
Value: []interface{}{"hello", "world"},
},
},
{
Name: "check NOT_IN",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.attr",
DataType: v3.AttributeKeyDataTypeUnspecified,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "NOT_IN",
Value: []interface{}{10, 20},
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.attr",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: true,
},
Operator: "NOT_IN",
Value: []interface{}{10, 20},
},
},
}
func TestJsonEnrich(t *testing.T) {
for _, tt := range testJSONFilterEnrichData {
Convey(tt.Name, t, func() {
res := jsonFilterEnrich(tt.Filter)
So(res, ShouldResemble, tt.Result)
})
}
}
func TestJsonReplaceField(t *testing.T) {
fields := map[string]v3.AttributeKey{
"method.name": {
Key: "method.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
},
"status": {
Key: "status",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeTag,
},
"data.error": {
Key: "data.error",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
IsColumn: true,
},
}
var TestJsonReplaceFieldData = []struct {
Name string
Filter v3.FilterItem
Result v3.FilterItem
}{
{
Name: "key in nested json",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.method.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "has",
Value: "index_service",
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "method.name",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
IsJSON: false,
},
Operator: "has",
Value: "index_service",
},
},
{
Name: "key at top level",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.status",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "=",
Value: 10,
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "status",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeTag,
IsJSON: false,
},
Operator: "=",
Value: 10,
},
},
{
Name: "key not present",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.status.code",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "=",
Value: 10,
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.status.code",
DataType: v3.AttributeKeyDataTypeInt64,
Type: v3.AttributeKeyTypeUnspecified,
IsJSON: false,
},
Operator: "=",
Value: 10,
},
},
{
Name: "key materialized",
Filter: v3.FilterItem{
Key: v3.AttributeKey{
Key: "body.data.error",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeUnspecified,
},
Operator: "=",
Value: 10,
},
Result: v3.FilterItem{
Key: v3.AttributeKey{
Key: "data.error",
DataType: v3.AttributeKeyDataTypeString,
Type: v3.AttributeKeyTypeTag,
IsJSON: false,
IsColumn: true,
},
Operator: "=",
Value: 10,
},
},
}
for _, tt := range TestJsonReplaceFieldData {
Convey(tt.Name, t, func() {
res := jsonReplaceField(tt.Filter, fields)
So(res, ShouldResemble, tt.Result)
})
}
}
var testParseStrValueData = []struct {
Name string
Operator v3.FilterOperator
Value interface{}
ResultType string
Result interface{}
}{
{
Name: "bool",
Value: "true",
Operator: v3.FilterOperatorEqual,
ResultType: "bool",
Result: true,
},
{
Name: "int",
Value: "10",
Operator: v3.FilterOperatorNotEqual,
ResultType: "int64",
Result: 10,
},
{
Name: "float",
Value: "10.0",
Operator: v3.FilterOperatorGreaterThan,
ResultType: "float64",
Result: 10.0,
},
{
Name: "string",
Value: "hello",
Operator: v3.FilterOperatorLessThan,
ResultType: "string",
Result: "hello",
},
}
func TestParseStrValue(t *testing.T) {
for _, tt := range testParseStrValueData {
Convey(tt.Name, t, func() {
vtype, value := parseStrValue(tt.Value.(string), tt.Operator)
So(vtype, ShouldEqual, tt.ResultType)
So(value, ShouldEqual, tt.Result)
})
}
}
func TestGetFieldNames(t *testing.T) {
type args struct {
compositeQuery *v3.CompositeQuery
}
tests := []struct {
name string
args args
want []string
}{
{
name: "nil composite query",
args: args{
compositeQuery: nil,
},
want: []string{},
},
{
name: "empty builder queries",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{},
},
},
want: []string{},
},
{
name: "aggregate attribute only",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "response_time",
},
},
},
},
},
want: []string{"response_time"},
},
{
name: "filters only",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
{Key: v3.AttributeKey{Key: "status"}, Value: "error", Operator: "!="},
},
},
},
},
},
},
want: []string{"user_name", "status"},
},
{
name: "JSON filters",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "body.user_name"}, Value: "john", Operator: "="},
{Key: v3.AttributeKey{Key: "body.status.code"}, Value: 200, Operator: "="},
{Key: v3.AttributeKey{Key: "body.requestor_list[*]"}, Value: "index_service", Operator: "has"},
},
},
},
},
},
},
// requestor_list[*] is not required though
want: []string{"user_name", "status.code", "requestor_list[*]"},
},
{
name: "groupBy only",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
GroupBy: []v3.AttributeKey{
{Key: "service_name"},
{Key: "host_name"},
},
},
},
},
},
want: []string{"service_name", "host_name"},
},
{
name: "orderBy only",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
OrderBy: []v3.OrderBy{
{ColumnName: "timestamp", Order: "DESC"},
{ColumnName: "response_time", Order: "ASC"},
},
},
},
},
},
want: []string{"timestamp", "response_time"},
},
{
name: "all field types combined",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "response_time",
},
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
{Key: v3.AttributeKey{Key: "body.status"}, Value: "error", Operator: "!="},
},
},
GroupBy: []v3.AttributeKey{
{Key: "service_name"},
{Key: "user_name"},
},
OrderBy: []v3.OrderBy{
{ColumnName: "timestamp", Order: "DESC"},
},
},
},
},
},
want: []string{"response_time", "user_name", "status", "service_name", "timestamp"},
},
{
name: "multiple builder queries",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"query1": {
QueryName: "query1",
Expression: "query1",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "field1",
},
},
"query2": {
QueryName: "query2",
Expression: "query2",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "field2"}, Value: "value", Operator: "="},
},
},
},
"query3": {
QueryName: "query3",
Expression: "different_expression",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "field3",
},
},
},
},
},
want: []string{"field1", "field2"},
},
{
name: "empty aggregate attribute key and filters",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
AggregateAttribute: v3.AttributeKey{
Key: "",
},
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
},
},
GroupBy: []v3.AttributeKey{
{Key: "service_name"},
},
},
},
},
},
want: []string{"user_name", "service_name"},
},
{
name: "JSON filter with array suffix - no use",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "body.tags[*]"}, Value: "important", Operator: "has"},
{Key: v3.AttributeKey{Key: "body.nested.array[*]"}, Value: "value", Operator: "has"},
},
},
},
},
},
},
want: []string{"tags[*]", "nested.array[*]"},
},
{
name: "mixed JSON and regular filters",
args: args{
compositeQuery: &v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"test": {
QueryName: "test",
Expression: "test",
DataSource: v3.DataSourceLogs,
Filters: &v3.FilterSet{
Operator: "AND",
Items: []v3.FilterItem{
{Key: v3.AttributeKey{Key: "user_name"}, Value: "john", Operator: "="},
{Key: v3.AttributeKey{Key: "body.status.abc"}, Value: "error", Operator: "!="},
{Key: v3.AttributeKey{Key: "service_name"}, Value: "api", Operator: "="},
},
},
},
},
},
},
want: []string{"user_name", "status.abc", "service_name"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := GetFieldNames(tt.args.compositeQuery)
// Sort both slices since order doesn't matter
sort.Strings(got)
sort.Strings(tt.want)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetFieldNames() = %v, want %v", got, tt.want)
}
})
}
}