gorm/search.go
Jason Lee 0e78f83efb Add paginate methods for query.
API same as [Kaminari](https://github.com/kaminari/kaminari)

before:

```go
page, _ := strconv.Atoi(ctx.Query("page"))
if page <= 0 {
        page = 1
}
perPage, _ := strconv.Atoi(ctx.Query("size"))
if perPage <= 0 {
        perPage = 20
}
offset := (page - 1) * perPage
scope := DB.Where("name = ?", "jinzhu").Offset(offset).Limit(perPage)
```

after:

```go
scope := DB.Where("name = ?", "jinzhu").MaxPerPage(50).
        Page(ctx.Query("page")).Per(ctx.Query("size"))
```
2020-01-28 10:18:45 +08:00

208 lines
4.6 KiB
Go

package gorm
import (
"fmt"
"strconv"
)
const (
defaultPerPage = 20
)
type search struct {
db *DB
whereConditions []map[string]interface{}
orConditions []map[string]interface{}
notConditions []map[string]interface{}
havingConditions []map[string]interface{}
joinConditions []map[string]interface{}
initAttrs []interface{}
assignAttrs []interface{}
selects map[string]interface{}
omits []string
orders []interface{}
preload []searchPreload
offset interface{}
limit interface{}
page interface{}
perPage interface{}
maxPerPage int
group string
tableName string
raw bool
Unscoped bool
ignoreOrderQuery bool
}
type searchPreload struct {
schema string
conditions []interface{}
}
func (s *search) clone() *search {
clone := *s
return &clone
}
func (s *search) Where(query interface{}, values ...interface{}) *search {
s.whereConditions = append(s.whereConditions, map[string]interface{}{"query": query, "args": values})
return s
}
func (s *search) Not(query interface{}, values ...interface{}) *search {
s.notConditions = append(s.notConditions, map[string]interface{}{"query": query, "args": values})
return s
}
func (s *search) Or(query interface{}, values ...interface{}) *search {
s.orConditions = append(s.orConditions, map[string]interface{}{"query": query, "args": values})
return s
}
func (s *search) Attrs(attrs ...interface{}) *search {
s.initAttrs = append(s.initAttrs, toSearchableMap(attrs...))
return s
}
func (s *search) Assign(attrs ...interface{}) *search {
s.assignAttrs = append(s.assignAttrs, toSearchableMap(attrs...))
return s
}
func (s *search) Order(value interface{}, reorder ...bool) *search {
if len(reorder) > 0 && reorder[0] {
s.orders = []interface{}{}
}
if value != nil && value != "" {
s.orders = append(s.orders, value)
}
return s
}
func (s *search) Select(query interface{}, args ...interface{}) *search {
s.selects = map[string]interface{}{"query": query, "args": args}
return s
}
func (s *search) Omit(columns ...string) *search {
s.omits = columns
return s
}
func (s *search) Limit(limit interface{}) *search {
s.limit = limit
return s
}
func (s *search) Offset(offset interface{}) *search {
s.offset = offset
return s
}
func (s *search) Per(perPage interface{}) *search {
s.perPage = perPage
s.pageToOffsetLimit()
return s
}
func (s *search) Page(page interface{}) *search {
s.page = page
s.pageToOffsetLimit()
return s
}
func (s *search) MaxPerPage(maxPerPage int) *search {
s.maxPerPage = maxPerPage
s.pageToOffsetLimit()
return s
}
func (s *search) pageToOffsetLimit() *search {
page, err := strconv.Atoi(fmt.Sprintf("%v", s.page))
if err != nil || page <= 0 {
page = 1
}
perPage, err := strconv.Atoi(fmt.Sprintf("%v", s.perPage))
if err != nil || perPage <= 0 {
perPage = defaultPerPage
}
if perPage > s.maxPerPage && s.maxPerPage > 0 {
perPage = s.maxPerPage
}
offset := (page - 1) * perPage
if offset < 0 {
offset = 0
}
s.page = page
s.perPage = perPage
s.offset = offset
s.limit = perPage
return s
}
func (s *search) Group(query string) *search {
s.group = s.getInterfaceAsSQL(query)
return s
}
func (s *search) Having(query interface{}, values ...interface{}) *search {
if val, ok := query.(*SqlExpr); ok {
s.havingConditions = append(s.havingConditions, map[string]interface{}{"query": val.expr, "args": val.args})
} else {
s.havingConditions = append(s.havingConditions, map[string]interface{}{"query": query, "args": values})
}
return s
}
func (s *search) Joins(query string, values ...interface{}) *search {
s.joinConditions = append(s.joinConditions, map[string]interface{}{"query": query, "args": values})
return s
}
func (s *search) Preload(schema string, values ...interface{}) *search {
var preloads []searchPreload
for _, preload := range s.preload {
if preload.schema != schema {
preloads = append(preloads, preload)
}
}
preloads = append(preloads, searchPreload{schema, values})
s.preload = preloads
return s
}
func (s *search) Raw(b bool) *search {
s.raw = b
return s
}
func (s *search) unscoped() *search {
s.Unscoped = true
return s
}
func (s *search) Table(name string) *search {
s.tableName = name
return s
}
func (s *search) getInterfaceAsSQL(value interface{}) (str string) {
switch value.(type) {
case string, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
str = fmt.Sprintf("%v", value)
default:
s.db.AddError(ErrInvalidSQL)
}
if str == "-1" {
return ""
}
return
}