mirror of
https://github.com/grafana/loki.git
synced 2026-03-13 09:33:58 +08:00
Signed-off-by: Robert Fratto <robertfratto@gmail.com> Co-authored-by: Ivan Kalita <ivan.kalita@grafana.com>
233 lines
6.8 KiB
Go
233 lines
6.8 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|