From 8e45c456241a0e11ea8517987fe91f6989ec18f4 Mon Sep 17 00:00:00 2001 From: Anton Nesterov Date: Thu, 8 Aug 2024 11:09:17 +0200 Subject: [PATCH] [wip] add missing filters Signed-off-by: Anton Nesterov --- pkg/dal/convert_test.go | 1 + pkg/dal/go.mod | 7 +++ pkg/filters/And.go | 13 +++--- pkg/filters/Like.go | 20 +++++++++ pkg/filters/NotIn.go | 31 +++++++++++++ pkg/filters/NotLike.go | 20 +++++++++ pkg/filters/Or.go | 22 ++++++++++ pkg/filters/registry.go | 5 +++ pkg/filters/unit_test.go | 95 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 pkg/dal/convert_test.go create mode 100644 pkg/dal/go.mod create mode 100644 pkg/filters/Like.go create mode 100644 pkg/filters/NotIn.go create mode 100644 pkg/filters/NotLike.go create mode 100644 pkg/filters/Or.go diff --git a/pkg/dal/convert_test.go b/pkg/dal/convert_test.go new file mode 100644 index 0000000..34f5247 --- /dev/null +++ b/pkg/dal/convert_test.go @@ -0,0 +1 @@ +package dal diff --git a/pkg/dal/go.mod b/pkg/dal/go.mod new file mode 100644 index 0000000..d9fc006 --- /dev/null +++ b/pkg/dal/go.mod @@ -0,0 +1,7 @@ +module l12.xyz/dal + +go 1.22.6 + +replace l12.xyz/dal/utils v0.0.0 => ../utils + +replace l12.xyz/dal/filters v0.0.0 => ../filters diff --git a/pkg/filters/And.go b/pkg/filters/And.go index cde9d11..65345c5 100644 --- a/pkg/filters/And.go +++ b/pkg/filters/And.go @@ -2,16 +2,19 @@ package filters import ( "fmt" + "strings" ) type And struct { - And []interface{} `json:"$and"` + And []string `json:"$and"` } -func (a And) ToSQLPart(ctx Context) string { - - fmt.Println(ctx, a) - return "" +func (f And) ToSQLPart(ctx Context) string { + if f.And == nil { + return "" + } + value := strings.Join(f.And, " AND ") + return fmt.Sprintf("(%s)", value) } func (a And) FromJSON(data interface{}) Filter { diff --git a/pkg/filters/Like.go b/pkg/filters/Like.go new file mode 100644 index 0000000..ca799be --- /dev/null +++ b/pkg/filters/Like.go @@ -0,0 +1,20 @@ +package filters + +import "fmt" + +type Like struct { + Like interface{} `json:"$like"` +} + +func (f Like) FromJSON(data interface{}) Filter { + return FromJson[Like](data) +} + +func (f Like) ToSQLPart(ctx Context) string { + if f.Like == nil { + return "" + } + name := ctx.GetFieldName() + value := ctx.NormalizeValue(f.Like) + return fmt.Sprintf("%s LIKE %v ESCAPE '\\'", name, value) +} diff --git a/pkg/filters/NotIn.go b/pkg/filters/NotIn.go new file mode 100644 index 0000000..29b76e3 --- /dev/null +++ b/pkg/filters/NotIn.go @@ -0,0 +1,31 @@ +package filters + +import ( + "fmt" + "strings" + + "l12.xyz/dal/utils" +) + +type NotIn struct { + NotIn []interface{} `json:"$nin"` +} + +func (f NotIn) FromJSON(data interface{}) Filter { + return FromJson[NotIn](data) +} + +func (f NotIn) ToSQLPart(ctx Context) string { + if f.NotIn == nil { + return "" + } + + name := ctx.GetFieldName() + values := utils.Map(f.NotIn, ctx.NormalizeValue) + data := make([]string, len(values)) + for i, v := range values { + data[i] = fmt.Sprintf("%v", v) + } + value := strings.Join(data, ", ") + return fmt.Sprintf("%s NOT IN (%v)", name, value) +} diff --git a/pkg/filters/NotLike.go b/pkg/filters/NotLike.go new file mode 100644 index 0000000..1e2564a --- /dev/null +++ b/pkg/filters/NotLike.go @@ -0,0 +1,20 @@ +package filters + +import "fmt" + +type NotLike struct { + NotLike interface{} `json:"$nlike"` +} + +func (f NotLike) FromJSON(data interface{}) Filter { + return FromJson[NotLike](data) +} + +func (f NotLike) ToSQLPart(ctx Context) string { + if f.NotLike == nil { + return "" + } + name := ctx.GetFieldName() + value := ctx.NormalizeValue(f.NotLike) + return fmt.Sprintf("%s NOT LIKE %v ESCAPE '\\'", name, value) +} diff --git a/pkg/filters/Or.go b/pkg/filters/Or.go new file mode 100644 index 0000000..e66ded1 --- /dev/null +++ b/pkg/filters/Or.go @@ -0,0 +1,22 @@ +package filters + +import ( + "fmt" + "strings" +) + +type Or struct { + Or []string `json:"$or"` +} + +func (f Or) ToSQLPart(ctx Context) string { + if f.Or == nil { + return "" + } + value := strings.Join(f.Or, " OR ") + return fmt.Sprintf("(%s)", value) +} + +func (a Or) FromJSON(data interface{}) Filter { + return FromJson[Or](data) +} diff --git a/pkg/filters/registry.go b/pkg/filters/registry.go index b64981c..cb4098d 100644 --- a/pkg/filters/registry.go +++ b/pkg/filters/registry.go @@ -1,6 +1,8 @@ package filters var FilterRegistry = map[string]Filter{ + "And": &And{}, + "Or": &Or{}, "Eq": &Eq{}, "Ne": &Ne{}, "Gt": &Gt{}, @@ -8,9 +10,12 @@ var FilterRegistry = map[string]Filter{ "Lt": &Lt{}, "Lte": &Lte{}, "In": &In{}, + "Nin": &NotIn{}, "Between": &Between{}, "NotBetween": &NotBetween{}, "Glob": &Glob{}, + "Like": &Like{}, + "NotLike": &NotLike{}, } func Convert(ctx Context, json interface{}) string { diff --git a/pkg/filters/unit_test.go b/pkg/filters/unit_test.go index b8ed979..0a5b1ce 100644 --- a/pkg/filters/unit_test.go +++ b/pkg/filters/unit_test.go @@ -20,6 +20,22 @@ func TestEq(t *testing.T) { } } +func TestGte(t *testing.T) { + ctx := SQLiteContext{ + TableAlias: "t", + FieldName: "test", + } + result := Convert(ctx, `{"$gte": 1}`) + resultMap := Convert(ctx, map[string]any{"$gte": 1}) + if result != `t.test >= 1` { + t.Errorf("Expected t.test >= 1, got %s", result) + } + if resultMap != result { + t.Log(resultMap) + t.Errorf("Expected resultMap to be equal to result") + } +} + func TestNe(t *testing.T) { ctx := SQLiteContext{ FieldName: "test", @@ -50,6 +66,21 @@ func TestBetween(t *testing.T) { } } +func TestNotBetween(t *testing.T) { + ctx := SQLiteContext{ + FieldName: "test", + } + result := Convert(ctx, `{"$nbetween": ["1", "5"]}`) + resultMap := Convert(ctx, map[string]any{"$nbetween": []string{"1", "5"}}) + if result != `test NOT BETWEEN '1' AND '5'` { + t.Errorf("Expected test BETWEEN '1' AND '5', got %s", result) + } + if resultMap != result { + t.Log(resultMap) + t.Errorf("Expected resultMap to be equal to result") + } +} + func TestGlob(t *testing.T) { ctx := SQLiteContext{ TableAlias: "t", @@ -81,3 +112,67 @@ func TestIn(t *testing.T) { t.Errorf("Expected resultMap to be equal to result") } } + +func TestNotIn(t *testing.T) { + ctx := SQLiteContext{ + TableAlias: "t", + FieldName: "test", + } + result := Convert(ctx, `{"$nin": [1, 2, 3]}`) + resultMap := Convert(ctx, map[string]any{"$nin": []int{1, 2, 3}}) + if result != `t.test NOT IN (1, 2, 3)` { + t.Errorf("Expected t.test NOT IN (1, 2, 3), got %s", result) + } + if resultMap != result { + t.Log(resultMap) + t.Errorf("Expected resultMap to be equal to result") + } +} + +func TestLike(t *testing.T) { + ctx := SQLiteContext{ + TableAlias: "t", + FieldName: "test", + } + result := Convert(ctx, `{"$like": "199_"}`) + resultMap := Convert(ctx, map[string]any{"$like": "199_"}) + if result != `t.test LIKE '199_' ESCAPE '\'` { + t.Errorf("Expected t.test LIKE '199_' ESCAPE '\\', got %s", result) + } + if resultMap != result { + t.Log(resultMap) + t.Errorf("Expected resultMap to be equal to result") + } +} + +func TestAnd(t *testing.T) { + ctx := SQLiteContext{ + TableAlias: "t", + FieldName: "test", + } + result := Convert(ctx, `{"$and": ["a > 0", "b < 10"]}`) + resultMap := Convert(ctx, map[string]any{"$and": []string{"a > 0", "b < 10"}}) + if result != `(a > 0 AND b < 10)` { + t.Errorf("Expected (a > 0 AND b < 10), got %s", result) + } + if resultMap != result { + t.Log(resultMap) + t.Errorf("Expected resultMap to be equal to result") + } +} + +func TestOr(t *testing.T) { + ctx := SQLiteContext{ + TableAlias: "t", + FieldName: "test", + } + result := Convert(ctx, `{"$or": ["a = 0", "b < 10"]}`) + resultMap := Convert(ctx, map[string]any{"$or": []string{"a = 0", "b < 10"}}) + if result != `(a = 0 OR b < 10)` { + t.Errorf("Expected (a > 0 OR b < 10), got %s", result) + } + if resultMap != result { + t.Log(resultMap) + t.Errorf("Expected resultMap to be equal to result") + } +}