Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: condition builder #101

Merged
merged 8 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Query.Filter EvaluableFunc / Evaluable for building better conditions…
… easier

Ticket: #97
  • Loading branch information
marino39 committed Apr 15, 2023
commit 06e41a29871725f29cf6aa7762af8071ed0c9fa3
6 changes: 6 additions & 0 deletions inspect/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ func (in *inspect) Query(ctx context.Context, table string, index string, indexS
tableValue := reflect.ValueOf(tableInfo)
queryValue := tableValue.MethodByName("Query").Call([]reflect.Value{})[0]

type queryEvalTypeFuncInterface interface {
EvaluableFuncType() reflect.Type
}
queryEvalFuncType := queryValue.Interface().(queryEvalTypeFuncInterface).EvaluableFuncType()

if indexInfo.Name() != bond.PrimaryIndexName {
indexValue := reflect.ValueOf(indexInfo)
indexSelectorValue := utils.MakeValue(tableInfo.EntryType())
Expand Down Expand Up @@ -130,6 +135,7 @@ func (in *inspect) Query(ctx context.Context, table string, index string, indexS
return []reflect.Value{reflect.ValueOf(ret)}
})

filterFunc = filterFunc.Convert(queryEvalFuncType)
queryValue = queryValue.MethodByName("Filter").Call([]reflect.Value{filterFunc})[0]
}

Expand Down
115 changes: 18 additions & 97 deletions query.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,104 +4,20 @@ import (
"bytes"
"context"
"fmt"
"reflect"
"sort"

"github.com/go-bond/bond/utils"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/constraints"
)

// Evaluable is the interface used to simplify Query.Filter usage
type Evaluable[R any] interface {
Eval(r R) bool
}

type Value[R any, V any] func(r R) V

type Eq[R any, V any] struct {
Record Value[R, V]
Equal V
}

func (e *Eq[R, V]) Eval(r R) bool {
return assert.ObjectsAreEqual(e.Record(r), e.Equal)
}

type Gt[R any, V constraints.Ordered] struct {
Record Value[R, V]
Greater V
}

func (g *Gt[R, V]) Eval(r R) bool {
return g.Record(r) > g.Greater
}
// EvaluableFunc is the function template to be used for record filtering.
type EvaluableFunc[R any] func(r R) bool

type Gte[R any, V constraints.Ordered] struct {
Record Value[R, V]
GreaterEqual V
// Eval implements Evaluable interface.
func (e EvaluableFunc[R]) Eval(r R) bool {
return e(r)
}

func (g *Gte[R, V]) Eval(r R) bool {
return g.Record(r) >= g.GreaterEqual
}

type Lt[R any, V constraints.Ordered] struct {
Record Value[R, V]
Less V
}

func (l *Lt[R, V]) Eval(r R) bool {
return l.Record(r) < l.Less
}

type Lte[R any, V constraints.Ordered] struct {
Record Value[R, V]
LessEqual V
}

func (l *Lte[R, V]) Eval(r R) bool {
return l.Record(r) <= l.LessEqual
}

type and[R any] []any

func (a *and[R]) Eval(r R) bool {
evalReturn := true
for _, evaluable := range *a {
evalReturn = evalReturn && evaluable.(Evaluable[R]).Eval(r)
}
return evalReturn
}

func And[R any](evalList ...Evaluable[R]) Evaluable[R] {
var e and[R]
for _, eval := range evalList {
e = append(e, eval)
}
return &e
}

type or[R any] []any

func (o *or[R]) Eval(r R) bool {
evalReturn := false
for _, evaluable := range *o {
evalReturn = evalReturn || evaluable.(Evaluable[R]).Eval(r)
}
return evalReturn
}

func Or[R any](evalList ...Evaluable[R]) Evaluable[R] {
var e or[R]
for _, eval := range evalList {
e = append(e, eval)
}
return &e
}

// FilterFunc is the function template to be used for record filtering.
type FilterFunc[R any] func(r R) bool

// OrderLessFunc is the function template to be used for record sorting.
type OrderLessFunc[R any] func(r, r2 R) bool

Expand All @@ -121,7 +37,7 @@ type Query[T any] struct {
indexSelector Selector[T]
reverse bool

filterFunc FilterFunc[T]
filterFunc EvaluableFunc[T]
orderLessFunc OrderLessFunc[T]
offset uint64
limit uint64
Expand Down Expand Up @@ -150,12 +66,17 @@ func (q Query[T]) Table() Table[T] {
return q.table
}

// EvaluableFuncType returns the type of the filter function.
func (q Query[T]) EvaluableFuncType() reflect.Type {
return reflect.TypeOf(q.filterFunc)
}

// With selects index for query execution. If not stated the default index will
// be used. The index need to be supplemented with a record selector that has
// indexed and order fields set. This is very important as selector also defines
// indexed _and order fields set. This is very important as selector also defines
// the row at which we start the query.
//
// WARNING: if we have DESC order on ID field, and we try to query with a selector
// WARNING: if we have DESC order on ID field, _and we try to query with a selector
// that has ID set to 0 it will start from the last row.
func (q Query[T]) With(idx *Index[T], selector Selector[T]) Query[T] {
q.index = idx
Expand All @@ -165,8 +86,8 @@ func (q Query[T]) With(idx *Index[T], selector Selector[T]) Query[T] {

// Filter adds additional filtering to the query. The conditions can be built with
// structures that implement Evaluable interface.
func (q Query[T]) Filter(filter FilterFunc[T]) Query[T] {
q.filterFunc = filter
func (q Query[T]) Filter(filter Evaluable[T]) Query[T] {
q.filterFunc = filter.Eval
return q
}

Expand All @@ -191,7 +112,7 @@ func (q Query[T]) Reverse() Query[T] {
// that are skipped. This may take a long time. Bond allows to use
// more efficient way to do that by passing last received row to
// With method as a selector. This will jump to that row instantly
// and start iterating from that point.
// _and start iterating from that point.
func (q Query[T]) Offset(offset uint64) Query[T] {
q.offset = offset
return q
Expand Down Expand Up @@ -290,7 +211,7 @@ func (q Query[T]) executeQuery(ctx context.Context, optBatch ...Batch) ([]T, err
return true, nil
}

// get and deserialize
// get _and deserialize
record, err := lazy.Get()
if err != nil {
return false, err
Expand Down
152 changes: 152 additions & 0 deletions query_eval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package bond

import (
"github.com/stretchr/testify/assert"
"golang.org/x/exp/constraints"
)

// Evaluable is the interface used to simplify Query.Filter usage
type Evaluable[R any] interface {
Eval(r R) bool
}

// Value is the interface used to simplify getting a value from a record
type Value[R any, V any] func(r R) V

type _eq[R any, V any] struct {
Record Value[R, V]
Equal V
}

// Eval returns true if the record value is equal to the given value
func (e *_eq[R, V]) Eval(r R) bool {
return assert.ObjectsAreEqual(e.Record(r), e.Equal)
}

// Eq returns an Evaluable that returns true if the record value is equal to the given value
func Eq[R any, V any](record Value[R, V], equal V) Evaluable[R] {
return &_eq[R, V]{record, equal}
}

type _gt[R any, V constraints.Ordered] struct {
Record Value[R, V]
Greater V
}

// Eval returns true if the record value is greater than the given value
func (g *_gt[R, V]) Eval(r R) bool {
return g.Record(r) > g.Greater
}

// Gt returns an Evaluable that returns true if the record value is greater than the given value
func Gt[R any, V constraints.Ordered](record Value[R, V], greater V) Evaluable[R] {
return &_gt[R, V]{record, greater}
}

type _gte[R any, V constraints.Ordered] struct {
Record Value[R, V]
GreaterEqual V
}

// Eval returns true if the record value is greater than or equal to the given value
func (g *_gte[R, V]) Eval(r R) bool {
return g.Record(r) >= g.GreaterEqual
}

// Gte returns an Evaluable that returns true if the record value is greater than or equal to the given value
func Gte[R any, V constraints.Ordered](record Value[R, V], greaterEqual V) Evaluable[R] {
return &_gte[R, V]{record, greaterEqual}
}

type _lt[R any, V constraints.Ordered] struct {
Record Value[R, V]
Less V
}

// Eval returns true if the record value is less than the given value
func (l *_lt[R, V]) Eval(r R) bool {
return l.Record(r) < l.Less
}

// Lt returns an Evaluable that returns true if the record value is less than the given value
func Lt[R any, V constraints.Ordered](record Value[R, V], less V) Evaluable[R] {
return &_lt[R, V]{record, less}
}

type _lte[R any, V constraints.Ordered] struct {
Record Value[R, V]
LessEqual V
}

// Eval returns true if the record value is less than or equal to the given value
func (l *_lte[R, V]) Eval(r R) bool {
return l.Record(r) <= l.LessEqual
}

// Lte returns an Evaluable that returns true if the record value is less than or equal to the given value
func Lte[R any, V constraints.Ordered](record Value[R, V], lessEqual V) Evaluable[R] {
return &_lte[R, V]{record, lessEqual}
}

type _and[R any] []any

// Eval returns true if all the Evaluable return true
func (a *_and[R]) Eval(r R) bool {
evalReturn := true
for _, evaluable := range *a {
evalReturn = evalReturn && evaluable.(Evaluable[R]).Eval(r)
}
return evalReturn
}

// And returns an Evaluable that returns true if all the Evaluable return true
func And[R any](evalList ...Evaluable[R]) Evaluable[R] {
var e _and[R]
for _, eval := range evalList {
e = append(e, eval)
}
return &e
}

type _or[R any] []any

// Eval returns true if any of the Evaluable return true
func (o *_or[R]) Eval(r R) bool {
evalReturn := false
for _, evaluable := range *o {
evalReturn = evalReturn || evaluable.(Evaluable[R]).Eval(r)
}
return evalReturn
}

// Or returns an Evaluable that returns true if any of the Evaluable return true
func Or[R any](evalList ...Evaluable[R]) Evaluable[R] {
var e _or[R]
for _, eval := range evalList {
e = append(e, eval)
}
return &e
}

type _not[R any] struct {
Evaluable Evaluable[R]
}

// Eval returns true if the Evaluable returns false
func (n *_not[R]) Eval(r R) bool {
return !n.Evaluable.Eval(r)
}

// Not returns an Evaluable that returns true if the Evaluable returns false
func Not[R any](eval Evaluable[R]) Evaluable[R] {
return &_not[R]{eval}
}

var _ Evaluable[any] = (*_eq[any, uint])(nil)
var _ Evaluable[any] = (*_gt[any, uint])(nil)
var _ Evaluable[any] = (*_gte[any, uint])(nil)
var _ Evaluable[any] = (*_lt[any, uint])(nil)
var _ Evaluable[any] = (*_lte[any, uint])(nil)
var _ Evaluable[any] = (*_and[any])(nil)
var _ Evaluable[any] = (*_or[any])(nil)
var _ Evaluable[any] = (*_not[any])(nil)
Loading