diff --git a/.circleci/config.yml b/.circleci/config.yml
index f39cbdd..ba41989 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,8 +2,8 @@ version: 2
jobs:
build:
docker:
- - image: golang:1.15-buster
- working_directory: /go/src/github.com/toguser/paginate
+ - image: golang:1.16-buster
+ working_directory: /go/src/github.com/morkid/paginate
steps:
- checkout
- run: go test -v
diff --git a/README.md b/README.md
index 107dc91..bd7cfcb 100644
--- a/README.md
+++ b/README.md
@@ -52,11 +52,9 @@ var req *http.Request = ...
// or
// var req *fasthttp.Request
-model := db.Where("id > ?", 1).Model(&Article{})
+stmt := db.Where("id > ?", 1).Model(&Article{})
pg := paginate.New()
-page := pg.Response(model, req, &[]Article{})
-// or
-page := pg.With(model).Request(req).Response(&[]Article{})
+page := pg.With(stmt).Request(req).Response(&[]Article{})
log.Println(page.Total)
log.Println(page.Items)
@@ -72,10 +70,6 @@ pg := paginate.New(&paginate.Config{
```
see more about [customize default configuration](#customize-default-configuration).
-> Note that `Response` was marked as a deprecated function. Please use `With` instead.
-> Old: `pg.Response(model, req, &[]Article{})`,
-> New: `pg.With(model).Request(req).Response(&[]Article{})`
-
## Pagination Result
```js
@@ -227,9 +221,9 @@ func main() {
pg := paginate.New()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- model := db.Joins("User").Model(&Article{})
- paginated := pg.Response(model, r, &[]Article{})
- j, _ := json.Marshal(paginated)
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(r).Response(&[]Article{})
+ j, _ := json.Marshal(page)
w.Header().Set("Content-type", "application/json")
w.Write(j)
})
@@ -253,9 +247,9 @@ func main() {
pg := paginate.New()
fasthttp.ListenAndServe(":3000", func(ctx *fasthttp.RequestCtx) {
- model := db.Joins("User").Model(&Article{})
- paginated := pg.Response(model, &ctx.Request, &[]Article{})
- j, _ := json.Marshal(paginated)
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(&ctx.Request).Response(&[]Article{})
+ j, _ := json.Marshal(page)
ctx.SetContentType("application/json")
ctx.SetBody(j)
})
@@ -276,9 +270,9 @@ func main() {
pg := paginate.New()
app := mux.NewRouter()
app.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
- model := db.Joins("User").Model(&Article{})
- paginated := pg.Response(model, req, &[]Article{})
- j, _ := json.Marshal(paginated)
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(req).Response(&[]Article{})
+ j, _ := json.Marshal(page)
w.Header().Set("Content-type", "application/json")
w.Write(j)
}).Methods("GET")
@@ -302,8 +296,9 @@ func main() {
pg := paginate.New()
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
- model := db.Joins("User").Model(&Article{})
- return c.JSON(pg.Response(model, c.Request(), &[]Article{}))
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(c.Request()).Response(&[]Article{})
+ return c.JSON(page)
})
app.Listen(":3000")
@@ -325,8 +320,9 @@ func main() {
pg := paginate.New()
app := echo.New()
app.GET("/", func(c echo.Context) error {
- model := db.Joins("User").Model(&Article{})
- return c.JSON(200, pg.Response(model, c.Request(), &[]Article{}))
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(c.Request()).Response(&[]Article{})
+ return c.JSON(200, page)
})
app.Logger.Fatal(app.Start(":3000"))
@@ -348,8 +344,9 @@ func main() {
pg := paginate.New()
app := gin.Default()
app.GET("/", func(c *gin.Context) {
- model := db.Joins("User").Model(&Article{})
- c.JSON(200, pg.Response(model, c.Request, &[]Article{}))
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(c.Request).Response(&[]Article{})
+ c.JSON(200, page)
})
app.Run(":3000")
}
@@ -372,8 +369,9 @@ func main() {
app := martini.Classic()
app.Use(render.Renderer())
app.Get("/", func(req *http.Request, r render.Render) {
- model := db.Joins("User").Model(&Article{})
- r.JSON(200, pg.Response(model, req, &[]Article{}))
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(req).Response(&[]Article{})
+ r.JSON(200, page)
})
app.Run()
}
@@ -392,9 +390,9 @@ func main() {
// var db *gorm.DB
pg := paginate.New()
web.Get("/", func(c *context.Context) {
- model := db.Joins("User").Model(&Article{})
- c.Output.JSON(
- pg.Response(model, c.Request, &[]Article{}), false, false)
+ stmt := db.Joins("User").Model(&Article{})
+ page := pg.With(stmt).Request(c.Request).Response(&[]Article{})
+ c.Output.JSON(page, false, false)
})
web.Run(":3000")
}
@@ -529,6 +527,7 @@ Single array member is known as **Logical Operator**.
// WHERE age = 20 OR age = 25
```
+
You are allowed to send array inside a value.
```js
["age", "between", [20, 30] ]
@@ -540,6 +539,19 @@ You are allowed to send array inside a value.
// WHERE age NOT IN(20, 21, 22, 23, 24, 25, 26, 26)
```
+Define chain columns with same value separated by comma.
+```js
+// Example 1
+["price,discount", ">", 10]
+// Produces:
+// WHERE price > 10 OR discount > 25
+
+// Example 2
+["deleted_at,expiration_date", null]
+// Produces:
+// WHERE deleted_at IS NULL OR expiration_date IS NULL
+```
+
You can filter nested condition with deep array.
```js
[
@@ -592,7 +604,9 @@ Config | Type | Default | Description
Operator | `string` | `OR` | Default conditional operator if no operator specified.
For example
`GET /user?filters=[["name","like","jo"],["age",">",20]]`,
produces
`SELECT * FROM user where name like '%jo' OR age > 20`
FieldWrapper | `string` | `LOWER(%s)` | FieldWrapper for `LIKE` operator *(for postgres default is: `LOWER((%s)::text)`)*
DefaultSize | `int64` | `10` | Default size or limit per page
-SmartSearch | `bool` | `false` | Enable smart search *(Experimental feature)*
+PageStart | `int64` | `0` | Set start page, default `0` if not set. `total_pages` , `max_page` and `page` variable will be affected if you set `PageStart` greater than `0`
+LikeAsIlikeDisabled | `bool` | `false` | By default, paginate using Case Insensitive on `LIKE` operator. Instead of using `ILIKE`, you can use `LIKE` operator to find what you want. You can set `LikeAsIlikeDisabled` to `true` if you need this feature to be disabled.
+SmartSearchEnabled | `bool` | `false` | Enable smart search *(Experimental feature)*
CustomParamEnabled | `bool` | `false` | Enable custom request parameter
FieldSelectorEnabled | `bool` | `false` | Enable partial response with specific fields. Comma separated per field. eg: `?fields=title,user.name`
SortParams | `[]string` | `[]string{"sort"}` | if `CustomParamEnabled` is `true`,
you can set the `SortParams` with custom parameter names.
For example: `[]string{"sorting", "ordering", "other_alternative_param"}`.
The following requests will capture same result
`?sorting=-name`
or `?ordering=-name`
or `?other_alternative_param=-name`
or `?sort=-name`
@@ -620,15 +634,15 @@ override := func(article *Article) {
}
var articles []Article
-model := db.Joins("User").Model(&Article{})
+stmt := db.Joins("User").Model(&Article{})
pg := paginate.New()
-result := pg.Response(model, httpRequest, &articles)
+page := pg.With(stmt).Request(httpRequest).Response(&articles)
for index := range articles {
override(&articles[index])
}
-log.Println(result.Items)
+log.Println(page.Items)
```
@@ -656,9 +670,9 @@ type UserNullable {
```go
// usage
nameAndIDOnly := []string{"name","id"}
-model := db.Model(&User{})
+stmt := db.Model(&User{})
-page := pg.With(model).
+page := pg.With(stmt).
Request(req).
Fields(nameAndIDOnly).
Response([]&UserNullable{})
@@ -706,7 +720,7 @@ func main() {
CacheAdapter: gocache.NewInMemoryCache(adapterConfig),
})
- page := pg.With(model).
+ page := pg.With(stmt).
Request(req).
Cache("article"). // set cache name
Response(&[]Article{})
@@ -731,7 +745,7 @@ func main() {
CacheAdapter: gocache.NewDiskCache(adapterConfig),
})
- page := pg.With(model).
+ page := pg.With(stmt).
Request(req).
Cache("article"). // set cache name
Response(&[]Article{})
@@ -763,7 +777,7 @@ func main() {
CacheAdapter: cache.NewRedisCache(adapterConfig),
})
- page := pg.With(model).
+ page := pg.With(stmt).
Request(req).
Cache("article").
Response(&[]Article{})
@@ -801,7 +815,7 @@ func main() {
CacheAdapter: cache.NewElasticCache(adapterConfig),
})
- page := pg.With(model).
+ page := pg.With(stmt).
Request(req).
Cache("article").
Response(&[]Article{})
diff --git a/go.mod b/go.mod
index 7a5889d..72bc22e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/morkid/paginate
-go 1.15
+go 1.16
require (
github.com/iancoleman/strcase v0.1.3
diff --git a/paginate.go b/paginate.go
index 382ccab..842f797 100644
--- a/paginate.go
+++ b/paginate.go
@@ -37,14 +37,6 @@ type Pagination struct {
Config *Config
}
-// Response return page of results
-//
-// Deprecated: Response must not be used. Use With instead
-func (p *Pagination) Response(stmt *gorm.DB, req interface{}, res interface{}) Page {
- fmt.Println("paginate.Response(stmt, req, res) is deprecated! please use paginate.With(stmt).Request(req).Response(res) instead")
- return p.With(stmt).Request(req).Response(res)
-}
-
// With func
func (p *Pagination) With(stmt *gorm.DB) RequestContext {
return reqContext{
@@ -116,14 +108,27 @@ func (r resContext) Response(res interface{}) Page {
if p.Config.DefaultSize == 0 {
p.Config.DefaultSize = 10
}
+ if p.Config.PageStart < 0 {
+ p.Config.PageStart = 0
+ }
- if p.Config.FieldWrapper == "" && p.Config.ValueWrapper == "" {
- defaultWrapper := "LOWER(%s)"
- wrappers := map[string]string{
+ defaultWrapper := "LOWER(%s)"
+ wrappers := map[string]string{
+ "sqlite": defaultWrapper,
+ "mysql": defaultWrapper,
+ "postgres": "LOWER((%s)::text)",
+ }
+
+ if p.Config.LikeAsIlikeDisabled {
+ defaultWrapper := "%s"
+ wrappers = map[string]string{
"sqlite": defaultWrapper,
"mysql": defaultWrapper,
- "postgres": "LOWER((%s)::text)",
+ "postgres": "(%s)::text",
}
+ }
+
+ if p.Config.FieldWrapper == "" && p.Config.ValueWrapper == "" {
p.Config.FieldWrapper = defaultWrapper
if wrapper, ok := wrappers[query.Dialector.Name()]; ok {
p.Config.FieldWrapper = wrapper
@@ -232,24 +237,20 @@ func (r resContext) Response(res interface{}) Page {
if math.Mod(f, 1.0) > 0 {
f = f + 1
}
+ f = math.Max(f, 1)
+
page.TotalPages = int64(f)
+ page.MaxPage = page.TotalPages - 1 + p.Config.PageStart
page.Page = int64(pr.Page)
page.Size = int64(pr.Size)
- page.MaxPage = 0
page.Visible = rs.RowsAffected
- if page.TotalPages > 0 {
- page.MaxPage = page.TotalPages - 1
- }
- if page.TotalPages < 1 {
- page.TotalPages = 1
- }
if page.Total < 1 {
- page.MaxPage = 0
+ page.MaxPage = p.Config.PageStart
page.TotalPages = 0
}
page.First = causes.Offset < 1
- page.Last = page.MaxPage == page.Page
+ page.Last = page.Page >= page.MaxPage
if hasAdapter && cKey != "" {
if cache, err := p.Config.JSONMarshal(page); nil == err {
@@ -331,7 +332,7 @@ func createCauses(p pageRequest) requestQuery {
}
query.Limit = p.Size
- query.Offset = p.Page * p.Size
+ query.Offset = (p.Page - int(p.Config.PageStart)) * p.Size
query.Wheres = wheres
query.WhereString = strings.Join(wheres, " ")
query.Sorts = sorts
@@ -363,7 +364,10 @@ func parsingNetHTTPRequest(r *http.Request, p *pageRequest) {
var postData map[string]string
if err := p.Config.JSONUnmarshal(body, &postData); nil == err {
generateParams(param, p.Config, func(key string) string {
- value, _ := postData[key]
+ value, exists := postData[key]
+ if !exists {
+ value = ""
+ }
return value
})
} else {
@@ -405,7 +409,10 @@ func parsingFastHTTPRequest(r *fasthttp.Request, p *pageRequest) {
var postData map[string]string
if err := p.Config.JSONUnmarshal(b, &postData); nil == err {
generateParams(param, p.Config, func(key string) string {
- value, _ := postData[key]
+ value, exists := postData[key]
+ if !exists {
+ value = ""
+ }
return value
})
} else {
@@ -447,7 +454,7 @@ func parsingQueryString(param *parameter, p *pageRequest) {
if i, e := strconv.Atoi(param.Page); nil == e {
p.Page = i
} else {
- p.Page = 0
+ p.Page = int(p.Config.PageStart)
}
if param.Sort != "" {
@@ -521,6 +528,10 @@ func arrayToFilter(arr []interface{}, config Config) pageFilters {
operatorEscape := regexp.MustCompile(`[^A-z=\<\>\-\+\^/\*%&! ]+`)
arrayLen := len(arr)
+ defaultOperator := config.Operator
+ if defaultOperator == "" {
+ defaultOperator = "OR"
+ }
if len(arr) > 0 {
subFilters := []pageFilters{}
@@ -545,9 +556,13 @@ func arrayToFilter(arr []interface{}, config Config) pageFilters {
}
} else if k == 1 {
filters.Value = i
- if nil == i {
+ if nil == i || reflect.TypeOf(i).Name() == "bool" {
filters.Operator = "IS"
}
+ if strings.Contains(filters.Column, ",") {
+ subFilters = filterToSubFilter(&filters, i, config)
+ continue
+ }
}
} else if arrayLen == 3 {
if k == 0 {
@@ -562,6 +577,10 @@ func arrayToFilter(arr []interface{}, config Config) pageFilters {
filters.Single = true
}
} else if k == 2 {
+ if strings.Contains(filters.Column, ",") {
+ subFilters = filterToSubFilter(&filters, i, config)
+ continue
+ }
switch filters.Operator {
case "LIKE", "ILIKE", "NOT LIKE", "NOT ILIKE":
escapeString := ""
@@ -579,11 +598,10 @@ func arrayToFilter(arr []interface{}, config Config) pageFilters {
}
value := fmt.Sprintf("%v", i)
re := regexp.MustCompile(escapePattern)
- value = string(re.ReplaceAll([]byte(value), []byte(escapeString+`$1`)))
- if config.SmartSearch {
+ value = re.ReplaceAllString(value, escapeString+`$1`)
+ if config.SmartSearchEnabled {
re := regexp.MustCompile(`[\s]+`)
- byt := re.ReplaceAll([]byte(value), []byte("%"))
- value = string(byt)
+ value = re.ReplaceAllString(value, "%")
}
filters.Value = fmt.Sprintf("%s%s%s", "%", value, "%")
default:
@@ -595,10 +613,6 @@ func arrayToFilter(arr []interface{}, config Config) pageFilters {
if len(subFilters) > 0 {
separatedSubFilters := []pageFilters{}
hasOperator := false
- defaultOperator := config.Operator
- if defaultOperator == "" {
- defaultOperator = "OR"
- }
for k, s := range subFilters {
if s.IsOperator && len(subFilters) == (k+1) {
break
@@ -615,12 +629,32 @@ func arrayToFilter(arr []interface{}, config Config) pageFilters {
}
filters.Value = separatedSubFilters
filters.Single = false
+ filters.IsOperator = false
}
}
return filters
}
+func filterToSubFilter(filters *pageFilters, value interface{}, config Config) []pageFilters {
+ subFilters := []pageFilters{}
+ re := regexp.MustCompile(`[^A-z0-9\._,]+`)
+ colString := re.ReplaceAllString(filters.Column, "")
+ columns := strings.Split(colString, ",")
+ columnRepeat := []interface{}{}
+ for _, col := range columns {
+ columnRepeat = append(columnRepeat, []interface{}{col, filters.Operator, value})
+ }
+
+ filters.Column = ""
+ filters.Single = false
+ filters.Operator = ""
+ filters.IsOperator = false
+ subFilters = append(subFilters, arrayToFilter(columnRepeat, config))
+
+ return subFilters
+}
+
//gocyclo:ignore
func generateWhereCauses(f pageFilters, config Config) ([]string, []interface{}) {
wheres := []string{}
@@ -703,7 +737,7 @@ func generateWhereCauses(f pageFilters, config Config) ([]string, []interface{})
if isStrValue {
if config.ValueWrapper != "" {
value = fmt.Sprintf(config.ValueWrapper, value)
- } else {
+ } else if !config.LikeAsIlikeDisabled {
value = strings.ToLower(value)
}
params = append(params, value)
@@ -781,7 +815,9 @@ type Config struct {
FieldWrapper string
ValueWrapper string
DefaultSize int64
- SmartSearch bool
+ PageStart int64
+ LikeAsIlikeDisabled bool
+ SmartSearchEnabled bool
Statement *gorm.Statement `json:"-"`
CustomParamEnabled bool
SortParams []string
diff --git a/paginate_test.go b/paginate_test.go
index e623601..e49e294 100644
--- a/paginate_test.go
+++ b/paginate_test.go
@@ -8,7 +8,10 @@ import (
"io"
"net/http"
"net/url"
+ "reflect"
+ "strings"
"testing"
+ "time"
"github.com/morkid/gocache"
"github.com/valyala/fasthttp"
@@ -286,12 +289,25 @@ func TestPaginate(t *testing.T) {
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
Logger: logger.Discard,
})
+ db.Exec("PRAGMA case_sensitive_like=ON;")
db.AutoMigrate(&User{}, &Article{})
users := []User{{Name: "John doe", AveragePoint: "Seventy %"}, {Name: "Jane doe", AveragePoint: "one hundred %"}}
articles := []Article{}
- articles = append(articles, Article{Title: "Written by john", Content: "Example by john", User: users[0]})
- articles = append(articles, Article{Title: "Written by jane", Content: "Example by jane", User: users[1]})
+
+ // add massive data
+ for i := 0; i < 50; i++ {
+ articles = append(articles, Article{
+ Title: fmt.Sprintf("Written by john %d", i),
+ Content: fmt.Sprintf("Example by john %d", i),
+ UserID: 1,
+ })
+ articles = append(articles, Article{
+ Title: fmt.Sprintf("Written by jane %d", i),
+ Content: fmt.Sprintf("Example by jane %d", i),
+ UserID: 2,
+ })
+ }
if nil != err {
t.Error(err.Error())
@@ -314,13 +330,16 @@ func TestPaginate(t *testing.T) {
return
}
+ // wait for transaction to finish
+ time.Sleep(1 * time.Second)
+
size := 1
page := 0
sort := "user.name,-id"
avg := "y %"
- data := "page=%d&size=%d&sort=%s&filters=%s"
+ data := "page=%v&size=%d&sort=%s&filters=%s"
- queryFilter := fmt.Sprintf(`[["user.average_point","like","%s"],["AND"],["user.name","IS NOT",null],["id","like","1"]]`, avg)
+ queryFilter := fmt.Sprintf(`[["user.average_point","like","%s"],["AND"],["user.name","IS NOT",null]]`, avg)
query := fmt.Sprintf(data, page, size, sort, url.QueryEscape(queryFilter))
request := &http.Request{
@@ -331,13 +350,17 @@ func TestPaginate(t *testing.T) {
}
response := []Article{}
- model := db.Joins("User").Model(&Article{})
- result := New().With(model).Request(request).Response(&response)
+ stmt := db.Joins("User").Model(&Article{})
+ result := New(&Config{LikeAsIlikeDisabled: true}).With(stmt).Request(request).Response(&response)
_, err = json.MarshalIndent(result, "", " ")
- if nil != err {
- t.Error(err)
- }
+ expectNil(t, err)
+ expect(t, result.Page, int64(0), "Invalid page")
+ expect(t, result.Total, int64(50), "Invalid total result")
+ expect(t, result.TotalPages, int64(50), "Invalid total pages")
+ expect(t, result.MaxPage, int64(49), "Invalid max page")
+ expectTrue(t, result.First, "Invalid first page")
+ expectFalse(t, result.Last, "Invalid last page")
queryFilter = fmt.Sprintf(`[["users.average_point","like","%s"],["AND"],["user.name","IS NOT",null],["id","like","1"]]`, avg)
query = fmt.Sprintf(data, page, size, sort, url.QueryEscape(queryFilter))
@@ -350,11 +373,51 @@ func TestPaginate(t *testing.T) {
}
response = []Article{}
- model = db.Joins("User").Model(&Article{})
- result = New(&Config{ErrorEnabled: true}).With(model).Request(request).Response(&response)
- if !result.Error {
- t.Error("Failed to get error message")
+ stmt = db.Joins("User").Model(&Article{})
+ result = New(&Config{ErrorEnabled: true}).With(stmt).Request(request).Response(&response)
+ expectTrue(t, result.Error, "Failed to get error message")
+
+ page = 1
+ size = 100
+ pageStart := int64(1)
+ query = fmt.Sprintf(data, page, size, sort, "")
+
+ request = &http.Request{
+ Method: "GET",
+ URL: &url.URL{
+ RawQuery: query,
+ },
+ }
+ response = []Article{}
+
+ stmt = db.Joins("User").Model(&Article{})
+ result = New(&Config{PageStart: pageStart}).With(stmt).Request(request).Response(&response)
+ expect(t, result.Page, int64(1), "Invalid page start")
+ expect(t, result.MaxPage, int64(1), "Invalid max page")
+ expect(t, len(response), 100, "Invalid total items")
+ expect(t, result.Total, int64(100), "Invalid total result")
+ expect(t, result.TotalPages, int64(1), "Invalid total pages")
+ expectTrue(t, result.First, "Invalid value first")
+ expectTrue(t, result.Last, "Invalid value last")
+
+ queryFilter = `[["user.average_point","like","y %"],["AND"],["user.name,title","LIKE","john"]]`
+ query = fmt.Sprintf(data, page, size, sort, url.QueryEscape(queryFilter))
+
+ request = &http.Request{
+ Method: "GET",
+ URL: &url.URL{
+ RawQuery: query,
+ },
}
+ response = []Article{}
+
+ stmt = db.Joins("User").Model(&Article{})
+ result = New(&Config{Operator: "AND", PageStart: pageStart, ErrorEnabled: true}).
+ With(stmt).Request(request).Response(&response)
+ expectFalse(t, result.Error, "An error occurred")
+ expect(t, result.Page, int64(1), "Invalid page start")
+ expect(t, result.MaxPage, int64(1), "Invalid max page")
+ expect(t, result.Total, int64(50), "Invalid max page")
}
type noOpAdapter struct {
@@ -461,8 +524,8 @@ func TestCache(t *testing.T) {
}
pg := New(config)
// set cache
- model1 := db.Joins("User").Model(&Article{}).Preload(`Category`)
- page1 := pg.With(model1).
+ stmt1 := db.Joins("User").Model(&Article{}).Preload(`Category`)
+ page1 := pg.With(stmt1).
Request(request).
Fields([]string{"id"}).
Cache("cache_prefix").
@@ -470,8 +533,8 @@ func TestCache(t *testing.T) {
// get cache
var cached []Article
- model2 := db.Joins("User").Model(&Article{})
- page2 := pg.With(model2).Request(request).Cache("cache_prefix").Response(&cached)
+ stmt2 := db.Joins("User").Model(&Article{})
+ page2 := pg.With(stmt2).Request(request).Cache("cache_prefix").Response(&cached)
if len(cached) < 1 {
t.Error("Cache pointer not working perfectly")
@@ -486,3 +549,86 @@ func TestCache(t *testing.T) {
pg.ClearAllCache()
pg.ClearAllCache()
}
+
+func expect(t *testing.T, expected interface{}, actual interface{}, message ...string) {
+ if expected != actual {
+ t.Errorf("%s: Expected %s(%v), got %s(%v)",
+ strings.Join(message, " "),
+ reflect.TypeOf(expected), expected,
+ reflect.TypeOf(actual), actual)
+ t.Fail()
+ }
+}
+
+func expectFalse(t *testing.T, actual bool, message ...string) {
+ expect(t, false, actual, message...)
+}
+
+func expectTrue(t *testing.T, actual bool, message ...string) {
+ expect(t, true, actual, message...)
+}
+
+func expectNil(t *testing.T, actual interface{}, message ...string) {
+ expect(t, nil, actual, message...)
+}
+
+func expectNotNil(t *testing.T, actual interface{}, message ...string) {
+ expect(t, false, actual == nil, message...)
+}
+
+func TestArrayFilter(t *testing.T) {
+ jsonString := `[
+ ["name,email,address", "like", "abc"]
+ ]`
+ var jsonData []interface{}
+ json.Unmarshal([]byte(jsonString), &jsonData)
+ filters := arrayToFilter(jsonData, Config{})
+
+ expectNotNil(t, filters)
+ expectNotNil(t, filters.Value)
+
+ subFilters, ok := filters.Value.([]pageFilters)
+ expectTrue(t, ok)
+ expect(t, 1, len(subFilters))
+
+ subFilterValues, ok := subFilters[0].Value.([]pageFilters)
+ expectTrue(t, ok)
+ expect(t, 1, len(subFilterValues))
+
+ contents, ok := subFilterValues[0].Value.([]pageFilters)
+ expectTrue(t, ok)
+ expect(t, 5, len(contents))
+
+ expect(t, "name", contents[0].Column)
+ expect(t, "LIKE", contents[0].Operator)
+ expect(t, "%abc%", contents[0].Value)
+
+ expect(t, "OR", contents[1].Operator)
+
+ expect(t, "email", contents[2].Column)
+ expect(t, "LIKE", contents[2].Operator)
+ expect(t, "%abc%", contents[2].Value)
+
+ expect(t, "OR", contents[3].Operator)
+
+ expect(t, "address", contents[4].Column)
+ expect(t, "LIKE", contents[4].Operator)
+ expect(t, "%abc%", contents[4].Value)
+}
+
+func TestGenerateWhereCauses(t *testing.T) {
+ jsonString := `[
+ ["name,email,address", "like", "abc"],
+ ["id", ">", 1]
+ ]`
+ var jsonData []interface{}
+ json.Unmarshal([]byte(jsonString), &jsonData)
+ filters := arrayToFilter(jsonData, Config{})
+ wheres, params := generateWhereCauses(filters, Config{})
+
+ where := strings.Join(wheres, " ")
+ where = strings.ReplaceAll(where, "( ", "(")
+ where = strings.ReplaceAll(where, " )", ")")
+ expect(t, "((((name LIKE ? OR email LIKE ? OR address LIKE ?))) OR (id > ?))", where)
+ expect(t, 4, len(params))
+}