Like Prometheus, but for logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
loki/pkg/expr/evaluate_test.go

232 lines
6.8 KiB

package expr_test
import (
"testing"
"github.com/grafana/regexp"
"github.com/stretchr/testify/require"
"github.com/grafana/loki/v3/pkg/columnar"
"github.com/grafana/loki/v3/pkg/columnar/columnartest"
"github.com/grafana/loki/v3/pkg/expr"
"github.com/grafana/loki/v3/pkg/memory"
)
// TestEvaluate performs a basic end-to-end test of expression evaluation.
func TestEvaluate(t *testing.T) {
var alloc memory.Allocator
record := columnar.NewRecordBatch(
columnar.NewSchema([]columnar.Column{
{Name: "name"},
{Name: "age"},
}),
3, // row count
[]columnar.Array{
columnartest.Array(t, columnar.KindUTF8, &alloc, "Peter", "Paul", "Mary"),
columnartest.Array(t, columnar.KindUint64, &alloc, 30, 25, 43),
},
)
// (name != "Paul" AND age > 25)
e := &expr.Binary{
Left: &expr.Binary{
Left: &expr.Column{Name: "name"},
Op: expr.BinaryOpNEQ,
Right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUTF8, "Paul")},
},
Op: expr.BinaryOpAND,
Right: &expr.Binary{
Left: &expr.Column{Name: "age"},
Op: expr.BinaryOpGT,
Right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 25)},
},
}
expect := columnartest.Array(t, columnar.KindBool, &alloc, true, false, true)
result, err := expr.Evaluate(&alloc, e, record)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, expect, result)
}
func TestEvaluate_Constant(t *testing.T) {
var alloc memory.Allocator
e := &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 42)}
expect := columnartest.Scalar(t, columnar.KindUint64, 42)
result, err := expr.Evaluate(&alloc, e, nil)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, expect, result)
}
func TestEvaluate_Column(t *testing.T) {
var alloc memory.Allocator
record := columnar.NewRecordBatch(
columnar.NewSchema([]columnar.Column{
{Name: "name"},
{Name: "age"},
{Name: "city"},
}),
3, // row count
[]columnar.Array{
columnartest.Array(t, columnar.KindUTF8, &alloc, "Alice", "Bob", "Charlie"),
columnartest.Array(t, columnar.KindUint64, &alloc, 30, 25, 35),
columnartest.Array(t, columnar.KindUTF8, &alloc, "NYC", "LA", "SF"),
},
)
t.Run("existing column", func(t *testing.T) {
e := &expr.Column{Name: "age"}
expect := columnartest.Array(t, columnar.KindUint64, &alloc, 30, 25, 35)
result, err := expr.Evaluate(&alloc, e, record)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, expect, result)
})
t.Run("non-existing column", func(t *testing.T) {
e := &expr.Column{Name: "nonexistent"}
expect := columnartest.Array(t, columnar.KindNull, &alloc, nil, nil, nil)
result, err := expr.Evaluate(&alloc, e, record)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, expect, result)
})
}
func TestEvaluate_Unary(t *testing.T) {
var alloc memory.Allocator
record := columnar.NewRecordBatch(
columnar.NewSchema([]columnar.Column{
{Name: "active"},
}),
3, // row count
[]columnar.Array{
columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
)
e := &expr.Unary{
Op: expr.UnaryOpNOT,
Value: &expr.Column{Name: "active"},
}
expect := columnartest.Array(t, columnar.KindBool, &alloc, false, true, false)
result, err := expr.Evaluate(&alloc, e, record)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, expect, result)
}
func TestEvaluate_Binary(t *testing.T) {
var alloc memory.Allocator
record := columnar.NewRecordBatch(
columnar.NewSchema([]columnar.Column{
{Name: "name"},
{Name: "age"},
{Name: "active"},
}),
3, // row count
[]columnar.Array{
columnartest.Array(t, columnar.KindUTF8, &alloc, "Alice", "Bob", "Charlie"),
columnartest.Array(t, columnar.KindUint64, &alloc, 30, 25, 35),
columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
)
tests := []struct {
op expr.BinaryOp
left expr.Expression
right expr.Expression
expect columnar.Datum
}{
{
op: expr.BinaryOpEQ,
left: &expr.Column{Name: "age"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 30)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, false),
},
{
op: expr.BinaryOpNEQ,
left: &expr.Column{Name: "age"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 30)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, false, true, true),
},
{
op: expr.BinaryOpGT,
left: &expr.Column{Name: "age"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 25)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
{
op: expr.BinaryOpGTE,
left: &expr.Column{Name: "age"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 30)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
{
op: expr.BinaryOpLT,
left: &expr.Column{Name: "age"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 30)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, false, true, false),
},
{
op: expr.BinaryOpLTE,
left: &expr.Column{Name: "age"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUint64, 30)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, true, false),
},
{
op: expr.BinaryOpAND,
left: &expr.Column{Name: "active"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindBool, true)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
{
op: expr.BinaryOpOR,
left: &expr.Column{Name: "active"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindBool, false)},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
{
op: expr.BinaryOpMatchRegex,
left: &expr.Column{Name: "name"},
right: &expr.Regexp{Expression: regexp.MustCompile("(?i)((al|ch).*)")},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
{
op: expr.BinaryOpHasSubstr,
left: &expr.Column{Name: "name"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUTF8, "li")},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
{
op: expr.BinaryOpHasSubstrIgnoreCase,
left: &expr.Column{Name: "name"},
right: &expr.Constant{Value: columnartest.Scalar(t, columnar.KindUTF8, "LI")},
expect: columnartest.Array(t, columnar.KindBool, &alloc, true, false, true),
},
}
for _, tt := range tests {
t.Run(tt.op.String(), func(t *testing.T) {
e := &expr.Binary{
Left: tt.left,
Op: tt.op,
Right: tt.right,
}
result, err := expr.Evaluate(&alloc, e, record)
require.NoError(t, err)
columnartest.RequireDatumsEqual(t, tt.expect, result)
})
}
}