diff --git a/expr.go b/expr.go index d4833f0..f7728f7 100644 --- a/expr.go +++ b/expr.go @@ -577,16 +577,8 @@ func (a *aggregator) iterGroup(ctx context.Context, node *Node, parsed *ParsedEx // XXX: Here we must add the OR groups to make group IDs a success. if len(node.Ors) > 0 { - for _, n := range node.Ors { - if !n.HasPredicate() || len(n.Ors) > 0 { - // Don't handle sub-branching for now. - // TODO: Recursively iterate. - stats.AddSlow() - continue - } - - all = append(all, n) - } + // Mark this as a mixed/slow expression to be fully tested. + stats.AddSlow() } if node.Predicate != nil { diff --git a/expr_test.go b/expr_test.go index 44e6cdd..82aed58 100644 --- a/expr_test.go +++ b/expr_test.go @@ -260,7 +260,7 @@ func TestEvaluate_Numbers(t *testing.T) { t.Run("With annoying floats", func(t *testing.T) { // This is the expected epression - expected := tex(`4.797009e+06 == event.data.id && (event.data.ts == null || event.data.ts > 1715211850340)`) + expected := tex(`4.797009e+06 == event.data.id && event.data.ts > 1715211850340`) // expected := tex(`event.data.id == 25`) loader := newEvalLoader() loader.AddEval(expected) @@ -322,7 +322,7 @@ func TestEvaluate_Numbers(t *testing.T) { t.Run("With floats", func(t *testing.T) { // This is the expected epression - expected := tex(`326909.0 == event.data.account_id && (event.data.ts == null || event.data.ts > 1714000000000)`) + expected := tex(`326909.0 == event.data.account_id && event.data.ts > 1714000000000`) // expected := tex(`event.data.id == 25`) loader := newEvalLoader() loader.AddEval(expected) @@ -599,6 +599,123 @@ func TestAggregateMatch(t *testing.T) { }) } +func TestOrs(t *testing.T) { + ctx := context.Background() + parser, err := newParser() + require.NoError(t, err) + + t.Run("simple ors", func(t *testing.T) { + loader := newEvalLoader() + e := NewAggregateEvaluator(parser, testBoolEvaluator, loader.Load, 0) + eval := tex(`event.a == "a" || event.b == "b"`) + loader.AddEval(eval) + ok, err := e.Add(ctx, eval) + require.NoError(t, err) + require.Equal(t, float64(1), ok) + + t.Run("it matches left", func(t *testing.T) { + + input := map[string]any{ + "event": map[string]any{ + "a": "a", + }, + } + evals, matched, err := e.Evaluate(ctx, input) + require.NoError(t, err) + require.EqualValues(t, 1, len(evals)) + require.EqualValues(t, 1, matched) + }) + + t.Run("it matches right", func(t *testing.T) { + + input := map[string]any{ + "event": map[string]any{ + "b": "b", + }, + } + evals, matched, err := e.Evaluate(ctx, input) + require.NoError(t, err) + require.EqualValues(t, 1, len(evals)) + require.EqualValues(t, 1, matched) + }) + }) + + t.Run("branched ors", func(t *testing.T) { + loader := newEvalLoader() + e := NewAggregateEvaluator(parser, testBoolEvaluator, loader.Load, 0) + eval := tex(`event.a == "a" && (event.b == "b" || event.c == "c")`) + loader.AddEval(eval) + ok, err := e.Add(ctx, eval) + require.NoError(t, err) + require.Equal(t, float64(0.5), ok) + + t.Run("it matches and + left", func(t *testing.T) { + + input := map[string]any{ + "event": map[string]any{ + "a": "a", + "b": "b", + }, + } + evals, matched, err := e.Evaluate(ctx, input) + require.NoError(t, err) + require.EqualValues(t, 1, len(evals)) + require.EqualValues(t, 1, matched) + }) + + t.Run("it matches and + right", func(t *testing.T) { + input := map[string]any{ + "event": map[string]any{ + "a": "a", + "c": "c", + }, + } + evals, matched, err := e.Evaluate(ctx, input) + require.NoError(t, err) + require.EqualValues(t, 1, len(evals)) + require.EqualValues(t, 1, matched) + }) + + t.Run("it matches neither", func(t *testing.T) { + input := map[string]any{ + "event": map[string]any{ + "a": "a", + "b": "", + "c": "", + }, + } + evals, matched, err := e.Evaluate(ctx, input) + require.NoError(t, err) + require.EqualValues(t, 0, len(evals)) + require.EqualValues(t, 1, matched) + + input = map[string]any{ + "event": map[string]any{ + "a": "", + "b": "b", + "c": "", + }, + } + evals, matched, err = e.Evaluate(ctx, input) + require.NoError(t, err) + require.EqualValues(t, 0, len(evals)) + require.EqualValues(t, 0, matched) + + input = map[string]any{ + "event": map[string]any{ + "a": "", + "b": "", + "c": "c", + }, + } + evals, matched, err = e.Evaluate(ctx, input) + require.NoError(t, err) + require.EqualValues(t, 0, len(evals)) + require.EqualValues(t, 0, matched) + }) + }) +} + func TestMacros(t *testing.T) { ctx := context.Background() parser, err := newParser() @@ -1021,17 +1138,17 @@ func TestMixedEngines(t *testing.T) { t.Run("Assert mixed engines", func(t *testing.T) { exprs := []string{ // each id has 1, 2, 3 as a TS - `event.data.id == "a" && (event.ts == null || event.ts > 1)`, - `event.data.id == "a" && (event.ts == null || event.ts > 2)`, - `event.data.id == "a" && (event.ts == null || event.ts > 3)`, + `event.data.id == "a" && event.ts > 1`, + `event.data.id == "a" && event.ts > 2`, + `event.data.id == "a" && event.ts > 3`, - `event.data.id == "b" && (event.ts == null || event.ts > 1)`, - `event.data.id == "b" && (event.ts == null || event.ts > 2)`, - `event.data.id == "b" && (event.ts == null || event.ts > 3)`, + `event.data.id == "b" && event.ts > 1`, + `event.data.id == "b" && event.ts > 2`, + `event.data.id == "b" && event.ts > 3`, - `event.data.id == "c" && (event.ts == null || event.ts > 1)`, - `event.data.id == "c" && (event.ts == null || event.ts > 2)`, - `event.data.id == "c" && (event.ts == null || event.ts > 3)`, + `event.data.id == "c" && event.ts > 1`, + `event.data.id == "c" && event.ts > 2`, + `event.data.id == "c" && event.ts > 3`, } for n, expr := range exprs { @@ -1068,7 +1185,7 @@ func TestMixedEngines(t *testing.T) { require.NoError(t, err) require.EqualValues(t, 1, len(eval)) require.EqualValues(t, 1, count) - require.Equal(t, `event.data.id == "a" && (event.ts == null || event.ts > 1)`, eval[0].GetExpression()) + require.Equal(t, `event.data.id == "a" && event.ts > 1`, eval[0].GetExpression()) // Should match the first and second "a" expr. eval, count, err = e.Evaluate(ctx, map[string]any{ @@ -1082,19 +1199,6 @@ func TestMixedEngines(t *testing.T) { require.NoError(t, err) require.EqualValues(t, 2, len(eval)) require.EqualValues(t, 2, count) - - // Null matches - eval, count, err = e.Evaluate(ctx, map[string]any{ - "event": map[string]any{ - "data": map[string]any{ - "id": "a", - }, - "ts": nil, - }, - }) - require.NoError(t, err) - require.EqualValues(t, 3, len(eval)) - require.EqualValues(t, 3, count) }) })