Skip to content

Commit

Permalink
Add lookup aggregation
Browse files Browse the repository at this point in the history
  • Loading branch information
WinPooh32 committed Jan 7, 2024
1 parent b49ad78 commit deb38aa
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 0 deletions.
79 changes: 79 additions & 0 deletions aggregation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package norm

import "context"

type Merge[M1, M2 any] struct {
L M1
R []M2
}

type Keyer[K comparable] interface {
Key() K
}

// Lookup performs left outer join.
func Lookup[
M1 ~[]T1,
M2 ~[]T2,
T1, T2 Keyer[K],
A1, A2 any,
K comparable,
](
ctx context.Context,
lhs Reader[M1, A1], lhsArgs A1,
rhs Reader[M2, A2], rhsArgs A2,
) (
merge []Merge[T1, T2],
err error,
) {
l, err := lhs.Read(ctx, lhsArgs)
if err != nil {
return nil, err
}

r, err := rhs.Read(ctx, rhsArgs)
if err != nil {
return nil, err
}

return _lookup[M1, M2, T1, T2, K](l, r), nil
}

func _lookup[
M1 ~[]T1,
M2 ~[]T2,
T1 Keyer[K],
T2 Keyer[K],
K comparable,
](
lhs M1,
rhs M2,
) (
out []Merge[T1, T2],
) {
out = make([]Merge[T1, T2], 0, len(lhs))
m := make(map[K][]int, len(rhs))

for i, v := range rhs {
k := v.Key()
m[k] = append(m[k], i)
}

for _, l := range lhs {
merge := Merge[T1, T2]{
L: l,
R: nil,
}

rii, ok := m[l.Key()]
if ok {
for _, i := range rii {
merge.R = append(merge.R, rhs[i])
}
}

out = append(out, merge)
}

return out
}
155 changes: 155 additions & 0 deletions aggregation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package norm

import (
"context"
"reflect"
"testing"
)

type kint int

func (v kint) Key() int {
return int(v)
}

type mem struct {
v []kint
}

func (m mem) Read(ctx context.Context, args struct{}) (value []kint, err error) {
return []kint(m.v), nil
}

func TestLookup(t *testing.T) {
type args struct {
ctx context.Context
lhs Reader[[]kint, struct{}]
lhsArgs struct{}
rhs Reader[[]kint, struct{}]
rhsArgs struct{}
}
tests := []struct {
name string
args args
wantMerge []Merge[kint, kint]
wantErr bool
}{
{
name: "with mathes",
args: args{
ctx: context.Background(),
lhs: mem{[]kint{1, 2, 3, 4, 5}},
lhsArgs: struct{}{},
rhs: mem{[]kint{5, 5, 2, 2, 4}},
rhsArgs: struct{}{},
},
wantMerge: []Merge[kint, kint]{
{1, nil},
{2, []kint{2, 2}},
{3, nil},
{4, []kint{4}},
{5, []kint{5, 5}},
},
wantErr: false,
},
{
name: "no match",
args: args{
ctx: context.Background(),
lhs: mem{[]kint{1, 2, 3, 4, 5}},
lhsArgs: struct{}{},
rhs: mem{[]kint{10, 11, 12, 13, 14}},
rhsArgs: struct{}{},
},
wantMerge: []Merge[kint, kint]{
{1, nil},
{2, nil},
{3, nil},
{4, nil},
{5, nil},
},
wantErr: false,
},
{
name: "right is empty",
args: args{
ctx: context.Background(),
lhs: mem{[]kint{1, 2, 3, 4, 5}},
lhsArgs: struct{}{},
rhs: mem{[]kint{}},
rhsArgs: struct{}{},
},
wantMerge: []Merge[kint, kint]{
{1, nil},
{2, nil},
{3, nil},
{4, nil},
{5, nil},
},
wantErr: false,
},
{
name: "right is nil",
args: args{
ctx: context.Background(),
lhs: mem{[]kint{1, 2, 3, 4, 5}},
lhsArgs: struct{}{},
rhs: mem{},
rhsArgs: struct{}{},
},
wantMerge: []Merge[kint, kint]{
{1, nil},
{2, nil},
{3, nil},
{4, nil},
{5, nil},
},
wantErr: false,
},
{
name: "left is empty",
args: args{
ctx: context.Background(),
lhs: mem{[]kint{}},
lhsArgs: struct{}{},
rhs: mem{[]kint{1, 2, 3, 4, 5}},
rhsArgs: struct{}{},
},
wantMerge: []Merge[kint, kint]{},
wantErr: false,
},
{
name: "left is nil",
args: args{
ctx: context.Background(),
lhs: mem{},
lhsArgs: struct{}{},
rhs: mem{[]kint{1, 2, 3, 4, 5}},
rhsArgs: struct{}{},
},
wantMerge: []Merge[kint, kint]{},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotMerge, err := Lookup[
[]kint, []kint,
kint, kint,
struct{}, struct{},
int,
](
tt.args.ctx,
tt.args.lhs, tt.args.lhsArgs,
tt.args.rhs, tt.args.rhsArgs,
)
if (err != nil) != tt.wantErr {
t.Errorf("Lookup() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotMerge, tt.wantMerge) {
t.Errorf("Lookup() = %v, want %v", gotMerge, tt.wantMerge)
}
})
}
}

0 comments on commit deb38aa

Please sign in to comment.