Skip to content

Commit f858975

Browse files
authored
Engine simplification (#37)
Engine simplification by mutating counts directly in matching engines.
1 parent 61a7a93 commit f858975

File tree

7 files changed

+261
-228
lines changed

7 files changed

+261
-228
lines changed

engine.go

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package expr
22

33
import (
44
"context"
5+
"sync"
56

67
"github.com/cespare/xxhash/v2"
78
"github.com/google/uuid"
@@ -18,6 +19,58 @@ const (
1819
// EngineTypeART
1920
)
2021

22+
func NewMatchResult() *MatchResult {
23+
return &MatchResult{
24+
Result: map[uuid.UUID]map[groupID]int{},
25+
Lock: &sync.Mutex{},
26+
}
27+
}
28+
29+
// MatchResult is a map of evaluable IDs to the groups found, and the number of elements
30+
// found matching that group.
31+
type MatchResult struct {
32+
Result map[uuid.UUID]map[groupID]int
33+
Lock *sync.Mutex
34+
}
35+
36+
func (m *MatchResult) Len() int {
37+
m.Lock.Lock()
38+
defer m.Lock.Unlock()
39+
return len(m.Result)
40+
}
41+
42+
// AddExprs increments the matched counter for the given eval's group ID
43+
func (m *MatchResult) Add(evalID uuid.UUID, gID groupID) {
44+
m.Lock.Lock()
45+
defer m.Lock.Unlock()
46+
if _, ok := m.Result[evalID]; !ok {
47+
m.Result[evalID] = map[groupID]int{}
48+
}
49+
m.Result[evalID][gID]++
50+
}
51+
52+
// AddExprs increments the matched counter for each stored expression part.
53+
func (m *MatchResult) AddExprs(exprs ...*StoredExpressionPart) {
54+
m.Lock.Lock()
55+
defer m.Lock.Unlock()
56+
for _, expr := range exprs {
57+
if _, ok := m.Result[expr.EvaluableID]; !ok {
58+
m.Result[expr.EvaluableID] = map[groupID]int{}
59+
}
60+
m.Result[expr.EvaluableID][expr.GroupID]++
61+
}
62+
}
63+
64+
// GroupMatches returns the total lenght of all matches for a given eval's group ID.
65+
func (m *MatchResult) GroupMatches(evalID uuid.UUID, gID groupID) int {
66+
m.Lock.Lock()
67+
defer m.Lock.Unlock()
68+
if _, ok := m.Result[evalID]; !ok {
69+
return 0
70+
}
71+
return m.Result[evalID][gID]
72+
}
73+
2174
// MatchingEngine represents an engine (such as a b-tree, radix trie, or
2275
// simple hash map) which matches a predicate over many expressions.
2376
type MatchingEngine interface {
@@ -31,7 +84,9 @@ type MatchingEngine interface {
3184
// expression parts received. Some may return false positives, but
3285
// each MatchingEngine should NEVER omit ExpressionParts which match
3386
// the given input.
34-
Match(ctx context.Context, input map[string]any) (matched []*StoredExpressionPart, err error)
87+
//
88+
// Note that the MatchResult is mutated on input.
89+
Match(ctx context.Context, input map[string]any, result *MatchResult) (err error)
3590

3691
// Add adds a new expression part to the matching engine for future matches.
3792
Add(ctx context.Context, p ExpressionPart) error
@@ -46,8 +101,8 @@ type MatchingEngine interface {
46101
// granularity of expression parts received. Some may return false positives by
47102
// ignoring the variable name. Note that each MatchingEngine should NEVER
48103
// omit ExpressionParts which match the given input; false positives are okay,
104+
Search(ctx context.Context, variable string, input any, result *MatchResult)
49105
// but not returning valid matches must be impossible.
50-
Search(ctx context.Context, variable string, input any) (matched []*StoredExpressionPart)
51106
}
52107

53108
// Leaf represents the leaf within a tree. This stores all expressions

engine_null.go

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ func (n *nullLookup) Type() EngineType {
3434
return EngineTypeNullMatch
3535
}
3636

37-
func (n *nullLookup) Match(ctx context.Context, data map[string]any) (matched []*StoredExpressionPart, err error) {
38-
l := &sync.Mutex{}
39-
matched = []*StoredExpressionPart{}
40-
37+
func (n *nullLookup) Match(ctx context.Context, data map[string]any, result *MatchResult) (err error) {
4138
pool := newErrPool(errPoolOpts{concurrency: n.concurrency})
4239

4340
for item := range n.paths {
@@ -55,32 +52,26 @@ func (n *nullLookup) Match(ctx context.Context, data map[string]any) (matched []
5552
res = []any{nil}
5653
}
5754

58-
// This matches null, nil (as null), and any non-null items.
59-
l.Lock()
60-
6155
// XXX: This engine hasn't been updated with denied items for !=. It needs consideration
6256
// in how to handle these cases appropriately.
63-
found := n.Search(ctx, path, res[0])
64-
matched = append(matched, found...)
65-
l.Unlock()
57+
n.Search(ctx, path, res[0], result)
6658

6759
return nil
6860
})
6961
}
7062

71-
return matched, pool.Wait()
63+
return pool.Wait()
7264
}
7365

74-
func (n *nullLookup) Search(ctx context.Context, variable string, input any) (matched []*StoredExpressionPart) {
66+
func (n *nullLookup) Search(ctx context.Context, variable string, input any, result *MatchResult) {
7567
if input == nil {
7668
// The input data is null, so the only items that can match are equality
7769
// comparisons to null.
78-
all := n.null[variable]
79-
return all
70+
result.AddExprs(n.null[variable]...)
71+
return
8072
}
8173

82-
all := n.not[variable]
83-
return all
74+
result.AddExprs(n.not[variable]...)
8475
}
8576

8677
func (n *nullLookup) Add(ctx context.Context, p ExpressionPart) error {

engine_number.go

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,7 @@ func (n numbers) Type() EngineType {
4141
return EngineTypeBTree
4242
}
4343

44-
func (n *numbers) Match(ctx context.Context, input map[string]any) (matched []*StoredExpressionPart, err error) {
45-
l := &sync.Mutex{}
46-
matched = []*StoredExpressionPart{}
47-
44+
func (n *numbers) Match(ctx context.Context, input map[string]any, result *MatchResult) (err error) {
4845
pool := newErrPool(errPoolOpts{concurrency: n.concurrency})
4946

5047
for item := range n.paths {
@@ -61,40 +58,19 @@ func (n *numbers) Match(ctx context.Context, input map[string]any) (matched []*S
6158
return nil
6259
}
6360

64-
var val float64
65-
switch v := res[0].(type) {
66-
case int:
67-
val = float64(v)
68-
case int64:
69-
val = float64(v)
70-
case float64:
71-
val = v
72-
default:
73-
return nil
74-
}
75-
7661
// This matches null, nil (as null), and any non-null items.
77-
l.Lock()
78-
found := n.Search(ctx, path, val)
79-
matched = append(matched, found...)
80-
l.Unlock()
62+
n.Search(ctx, path, res[0], result)
8163

8264
return nil
8365
})
8466
}
8567

86-
return matched, pool.Wait()
68+
return pool.Wait()
8769
}
8870

8971
// Search returns all ExpressionParts which match the given input, ignoring the variable name
9072
// entirely.
91-
func (n *numbers) Search(ctx context.Context, variable string, input any) (matched []*StoredExpressionPart) {
92-
n.lock.RLock()
93-
defer n.lock.RUnlock()
94-
95-
// initialize matched
96-
matched = []*StoredExpressionPart{}
97-
73+
func (n *numbers) Search(ctx context.Context, variable string, input any, result *MatchResult) {
9874
var val float64
9975

10076
switch v := input.(type) {
@@ -105,7 +81,7 @@ func (n *numbers) Search(ctx context.Context, variable string, input any) (match
10581
case float64:
10682
val = v
10783
default:
108-
return nil
84+
return
10985
}
11086

11187
// First, find exact matches.
@@ -115,8 +91,9 @@ func (n *numbers) Search(ctx context.Context, variable string, input any) (match
11591
if m.Ident != nil && *m.Ident != variable {
11692
continue
11793
}
118-
// This is a candidatre.
119-
matched = append(matched, m)
94+
95+
// This is a candidate.
96+
result.AddExprs(m)
12097
}
12198
}
12299

@@ -131,8 +108,8 @@ func (n *numbers) Search(ctx context.Context, variable string, input any) (match
131108
if m.Ident != nil && *m.Ident != variable {
132109
continue
133110
}
134-
// This is a candidatre.
135-
matched = append(matched, m)
111+
// This is a candidate.
112+
result.AddExprs(m)
136113
}
137114
return true
138115
})
@@ -148,13 +125,11 @@ func (n *numbers) Search(ctx context.Context, variable string, input any) (match
148125
if m.Ident != nil && *m.Ident != variable {
149126
continue
150127
}
151-
// This is a candidatre.
152-
matched = append(matched, m)
128+
// This is a candidate.
129+
result.AddExprs(m)
153130
}
154131
return true
155132
})
156-
157-
return matched
158133
}
159134

160135
func (n *numbers) Add(ctx context.Context, p ExpressionPart) error {

0 commit comments

Comments
 (0)