mirror of
https://github.com/grafana/loki.git
synced 2026-03-13 09:33:58 +08:00
Introduces selection vectors (bitmaps) for efficient row-level filtering in columnar operations without materializing filtered subsets.
163 lines
5.4 KiB
Go
163 lines
5.4 KiB
Go
package expr
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/grafana/loki/v3/pkg/columnar"
|
|
"github.com/grafana/loki/v3/pkg/compute"
|
|
"github.com/grafana/loki/v3/pkg/memory"
|
|
)
|
|
|
|
// Evaluate processes expr against the provided batch, producing a datum as a
|
|
// result using alloc.
|
|
//
|
|
// The return type of Evaluate depends on the expression provided. See the
|
|
// documentation for implementations of Expression for what they produce when
|
|
// evaluated.
|
|
func Evaluate(alloc *memory.Allocator, expr Expression, batch *columnar.RecordBatch) (columnar.Datum, error) {
|
|
// Selection defines which rows are evaluated. Selected rows have a true value
|
|
// in the selection vector. When selection has Len() == 0, all rows are selected.
|
|
// When selection has Len() > 0, only rows with a true bit are selected. Rows that
|
|
// are not selected are treated as null in array results.
|
|
//
|
|
// We hide selection vector from the caller because we don't support selection on expressions that are columns.
|
|
// E.g., evaluateWithSelection(alloc, &expr.Column{...}, batch, onlyFirstAndThirdRows) will return all the rows.
|
|
// This is not a problem though as selection vector can be not "allSelected" only when the expression contains a
|
|
// BinOp that can be short-circuited.
|
|
allSelected := memory.Bitmap{}
|
|
|
|
return evaluateWithSelection(alloc, expr, batch, allSelected)
|
|
}
|
|
|
|
func evaluateWithSelection(alloc *memory.Allocator, expr Expression, batch *columnar.RecordBatch, selection memory.Bitmap) (columnar.Datum, error) {
|
|
nrows := 0
|
|
if batch != nil {
|
|
nrows = int(batch.NumRows())
|
|
}
|
|
if selection.Len() > 0 && selection.Len() != nrows {
|
|
return nil, fmt.Errorf("selection length mismatch: %d != %d", selection.Len(), nrows)
|
|
}
|
|
|
|
switch expr := expr.(type) {
|
|
case *Constant:
|
|
return expr.Value, nil
|
|
|
|
case *Column:
|
|
columnIndex := -1
|
|
if schema := batch.Schema(); schema != nil {
|
|
_, columnIndex = schema.ColumnIndex(expr.Name)
|
|
}
|
|
|
|
if columnIndex == -1 {
|
|
validity := memory.NewBitmap(alloc, int(batch.NumRows()))
|
|
validity.AppendCount(false, int(batch.NumRows()))
|
|
return columnar.NewNull(validity), nil
|
|
}
|
|
|
|
return batch.Column(int64(columnIndex)), nil
|
|
|
|
case *Unary:
|
|
return evaluateUnary(alloc, expr, batch, selection)
|
|
|
|
case *Binary:
|
|
return evaluateBinary(alloc, expr, batch, selection)
|
|
|
|
case *Regexp:
|
|
return nil, fmt.Errorf("regexp can only be evaluated as the right-hand side of regex match operations")
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unexpected expression type %T", expr))
|
|
}
|
|
}
|
|
|
|
func evaluateUnary(alloc *memory.Allocator, expr *Unary, batch *columnar.RecordBatch, selection memory.Bitmap) (columnar.Datum, error) {
|
|
switch expr.Op {
|
|
case UnaryOpNOT:
|
|
value, err := evaluateWithSelection(alloc, expr.Value, batch, selection)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return compute.Not(alloc, value, selection)
|
|
default:
|
|
return nil, fmt.Errorf("unexpected unary operator %s", expr.Op)
|
|
}
|
|
}
|
|
|
|
func evaluateBinary(alloc *memory.Allocator, expr *Binary, batch *columnar.RecordBatch, selection memory.Bitmap) (columnar.Datum, error) {
|
|
// Check for special operators that need different handling of their arguments.
|
|
switch expr.Op {
|
|
case BinaryOpMatchRegex, BinaryOpIn:
|
|
return evaluateSpecialBinary(alloc, expr, batch, selection)
|
|
}
|
|
|
|
// TODO(rfratto): If expr.Op is [BinaryOpAND] or [BinaryOpOR], we can
|
|
// propagate selection vectors to avoid unnecessary evaluations.
|
|
left, err := evaluateWithSelection(alloc, expr.Left, batch, selection)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
right, err := evaluateWithSelection(alloc, expr.Right, batch, selection)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch expr.Op {
|
|
case BinaryOpEQ:
|
|
return compute.Equals(alloc, left, right, selection)
|
|
case BinaryOpNEQ:
|
|
return compute.NotEquals(alloc, left, right, selection)
|
|
case BinaryOpGT:
|
|
return compute.GreaterThan(alloc, left, right, selection)
|
|
case BinaryOpGTE:
|
|
return compute.GreaterOrEqual(alloc, left, right, selection)
|
|
case BinaryOpLT:
|
|
return compute.LessThan(alloc, left, right, selection)
|
|
case BinaryOpLTE:
|
|
return compute.LessOrEqual(alloc, left, right, selection)
|
|
case BinaryOpAND:
|
|
return compute.And(alloc, left, right, selection)
|
|
case BinaryOpOR:
|
|
return compute.Or(alloc, left, right, selection)
|
|
case BinaryOpHasSubstr:
|
|
return compute.Substr(alloc, left, right, selection)
|
|
case BinaryOpHasSubstrIgnoreCase:
|
|
return compute.SubstrInsensitive(alloc, left, right, selection)
|
|
default:
|
|
return nil, fmt.Errorf("unexpected binary operator %s", expr.Op)
|
|
}
|
|
}
|
|
|
|
// evaluateSpecialBinary evaluates binary expressions for which one of the
|
|
// arguments does not evaluate into an expression of its own.
|
|
func evaluateSpecialBinary(alloc *memory.Allocator, expr *Binary, batch *columnar.RecordBatch, selection memory.Bitmap) (columnar.Datum, error) {
|
|
switch expr.Op {
|
|
case BinaryOpMatchRegex:
|
|
left, err := evaluateWithSelection(alloc, expr.Left, batch, selection)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
right, ok := expr.Right.(*Regexp)
|
|
if !ok {
|
|
return nil, fmt.Errorf("right-hand side of regex match operation must be a regexp, got %T", expr.Right)
|
|
}
|
|
|
|
return compute.RegexpMatch(alloc, left, right.Expression, selection)
|
|
case BinaryOpIn:
|
|
left, err := evaluateWithSelection(alloc, expr.Left, batch, selection)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
right, ok := expr.Right.(*ValueSet)
|
|
if !ok {
|
|
return nil, fmt.Errorf("right-hand side of in operation must be a ValueSet, got %T", expr.Right)
|
|
}
|
|
|
|
return compute.IsMember(alloc, left, right.Values, selection)
|
|
}
|
|
|
|
return nil, fmt.Errorf("unexpected binary operator %s", expr.Op)
|
|
}
|