Skip to content

Commit

Permalink
feat(multifilter): Adding a multi filter to handle multiple filters (#98
Browse files Browse the repository at this point in the history
)

* adding a multifilter

* adding mocks and examples
  • Loading branch information
Jacobbrewer1 authored Jan 22, 2025
1 parent de92993 commit 52ec1ab
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 20 deletions.
4 changes: 4 additions & 0 deletions examples/multifilter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Multi-Filter

The `multifilter` example demonstrates how to use the `multifilter`. The `multifilter` effectively is the same as using
multiple `patcher.WithWhere` filters, but it is more efficient and potentially more readable.
36 changes: 36 additions & 0 deletions examples/multifilter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"fmt"

"github.com/jacobbrewer1/patcher"
)

type ExampleFilter struct {
Field string
Value any
}

func (e ExampleFilter) Where() (string, []any) {
return fmt.Sprintf("%s = ?", e.Field), []any{e.Value}
}

func main() {
// Create example filters
filters := []ExampleFilter{
{Field: "name", Value: "John"},
{Field: "age", Value: 30},
}

mf := patcher.NewMultiFilter()

// Append each filter to the WHERE clause
for _, filter := range filters {
mf.Add(filter)
}

// Print the WHERE clause
sql, args := mf.Where()
fmt.Printf("WHERE SQL:\n%s\n", sql)
fmt.Printf("WHERE Args: %v\n", args)
}
12 changes: 12 additions & 0 deletions joiner.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
package patcher

import "strings"

// Joiner is an interface that can be used to specify the JOIN clause to use when the SQL is being generated.
type Joiner interface {
Join() (string, []any)
}

func appendJoin(join Joiner, builder *strings.Builder, args *[]any) {
jSQL, jArgs := join.Join()
if jArgs == nil {
jArgs = make([]any, 0)
}
builder.WriteString(strings.TrimSpace(jSQL))
builder.WriteString("\n")
*args = append(*args, jArgs...)
}
59 changes: 59 additions & 0 deletions mock_MultiFilter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions multifilter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package patcher

import "strings"

type MultiFilter interface {
Wherer
Add(where Wherer)
}

type multiFilter struct {
whereSql *strings.Builder
whereArgs []any
}

func NewMultiFilter() MultiFilter {
return &multiFilter{
whereSql: new(strings.Builder),
whereArgs: nil,
}
}

func (m *multiFilter) Add(where Wherer) {
appendWhere(where, m.whereSql, &m.whereArgs)
}

func (m *multiFilter) Where() (string, []any) {
return m.whereSql.String(), m.whereArgs
}
63 changes: 63 additions & 0 deletions multifilter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package patcher

import (
"testing"

"github.com/stretchr/testify/suite"
)

type multiFilterSuite struct {
suite.Suite
}

func TestMultiFilterSuite(t *testing.T) {
suite.Run(t, new(multiFilterSuite))
}

func (s *multiFilterSuite) TestNewMultiFilter_Add_Single() {
mf := NewMultiFilter()
s.NotNil(mf)

mw := NewMockWherer(s.T())
mw.On("Where").Return("where", []any{"arg1", "arg2"})
mf.Add(mw)

sql, args := mf.Where()
s.Equal("AND where\n", sql)
s.Equal([]any{"arg1", "arg2"}, args)
}

func (s *multiFilterSuite) TestNewMultiFilter_Add_Multi() {
mf := NewMultiFilter()
s.NotNil(mf)

mw := NewMockWherer(s.T())
mw.On("Where").Return("where", []any{"arg1", "arg2"})
mf.Add(mw)

mwTwo := NewMockWherer(s.T())
mwTwo.On("Where").Return("whereTwo", []any{"arg3", "arg4"})
mf.Add(mwTwo)

sql, args := mf.Where()
s.Equal("AND where\nAND whereTwo\n", sql)
s.Equal([]any{"arg1", "arg2", "arg3", "arg4"}, args)
}

func (s *multiFilterSuite) TestNewMultiFilter_Add_WhereTyper() {
mf := NewMultiFilter()
s.NotNil(mf)

mw := NewMockWherer(s.T())
mw.On("Where").Return("where", []any{"arg1", "arg2"})
mf.Add(mw)

mwt := NewMockWhereTyper(s.T())
mwt.On("Where").Return("whereTwo", []any{"arg3", "arg4"})
mwt.On("WhereType").Return(WhereTypeOr)
mf.Add(mwt)

sql, args := mf.Where()
s.Equal("AND where\nOR whereTwo\n", sql)
s.Equal([]any{"arg1", "arg2", "arg3", "arg4"}, args)
}
22 changes: 2 additions & 20 deletions patch_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,14 @@ func WithTable(table string) PatchOpt {
// WithWhere sets the where clause to use in the SQL statement
func WithWhere(where Wherer) PatchOpt {
return func(s *SQLPatch) {
fwSQL, fwArgs := where.Where()
if fwArgs == nil {
fwArgs = make([]any, 0)
}
wtStr := WhereTypeAnd // default to AND
wt, ok := where.(WhereTyper)
if ok && wt.WhereType().IsValid() {
wtStr = wt.WhereType()
}
s.whereSql.WriteString(string(wtStr) + " ")
s.whereSql.WriteString(strings.TrimSpace(fwSQL))
s.whereSql.WriteString("\n")
s.whereArgs = append(s.whereArgs, fwArgs...)
appendWhere(where, s.whereSql, &s.whereArgs)
}
}

// WithJoin sets the join clause to use in the SQL statement
func WithJoin(join Joiner) PatchOpt {
return func(s *SQLPatch) {
fjSQL, fjArgs := join.Join()
if fjArgs == nil {
fjArgs = make([]any, 0)
}
s.joinSql.WriteString(strings.TrimSpace(fjSQL))
s.joinSql.WriteString("\n")
s.joinArgs = append(s.joinArgs, fjArgs...)
appendJoin(join, s.joinSql, &s.joinArgs)
}
}

Expand Down
20 changes: 20 additions & 0 deletions sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ func (s *newSQLPatchSuite) TestNewSQLPatch_Success() {
s.Equal([]any{int64(1), "test"}, patch.args)
}

func (s *newSQLPatchSuite) TestNewSQLPatch_Success_MultiFilter() {
type testObj struct {
Id *int `db:"id_tag"`
Name *string `db:"name_tag"`
}

obj := testObj{
Id: ptr(1),
Name: ptr("test"),
}

mf := NewMockMultiFilter(s.T())
mf.On("Where").Return("where", []any{"arg1", "arg2"})

patch := NewSQLPatch(obj, WithWhere(mf))

s.Equal([]string{"id_tag = ?", "name_tag = ?"}, patch.fields)
s.Equal([]any{int64(1), "test"}, patch.args)
}

func (s *newSQLPatchSuite) TestNewSQLPatch_Fields_Args_Getters() {
type testObj struct {
Id *int `db:"id_tag"`
Expand Down
18 changes: 18 additions & 0 deletions wherer.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package patcher

import "strings"

// Wherer is an interface that can be used to specify the WHERE clause to use. By using this interface,
// the package will default to using an "AND" WHERE clause. If you want to use an "OR" WHERE clause, you can
// use the WhereTyper interface instead.
Expand Down Expand Up @@ -28,3 +30,19 @@ func (w WhereType) IsValid() bool {
}
return false
}

func appendWhere(where Wherer, builder *strings.Builder, args *[]any) {
wSQL, fwArgs := where.Where()
if fwArgs == nil {
fwArgs = make([]any, 0)
}
wtStr := WhereTypeAnd // default to AND
wt, ok := where.(WhereTyper)
if ok && wt.WhereType().IsValid() {
wtStr = wt.WhereType()
}
builder.WriteString(string(wtStr) + " ")
builder.WriteString(strings.TrimSpace(wSQL))
builder.WriteString("\n")
*args = append(*args, fwArgs...)
}

0 comments on commit 52ec1ab

Please sign in to comment.