/
paginator.go
118 lines (105 loc) 路 3.32 KB
/
paginator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package database
import (
"math"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// Paginator structure containing pagination information and result records.
// Can be sent to the client directly.
type Paginator struct {
DB *gorm.DB `json:"-"`
Records interface{} `json:"records"`
rawQuery string
rawQueryVars []interface{}
rawCountQuery string
rawCountQueryVars []interface{}
MaxPage int64 `json:"maxPage"`
Total int64 `json:"total"`
PageSize int `json:"pageSize"`
CurrentPage int `json:"currentPage"`
loadedPageInfo bool
}
func paginateScope(page, pageSize int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
// NewPaginator create a new Paginator.
//
// Given DB transaction can contain clauses already, such as WHERE, if you want to
// filter results.
//
// articles := []model.Article{}
// tx := database.Conn().Where("title LIKE ?", "%"+sqlutil.EscapeLike(search)+"%")
// paginator := database.NewPaginator(tx, page, pageSize, &articles)
// result := paginator.Find()
// if response.HandleDatabaseError(result) {
// response.JSON(http.StatusOK, paginator)
// }
func NewPaginator(db *gorm.DB, page, pageSize int, dest interface{}) *Paginator {
return &Paginator{
DB: db,
CurrentPage: page,
PageSize: pageSize,
Records: dest,
}
}
// Raw set a raw SQL query and count query.
// The Paginator will execute the raw queries instead of automatically creating them.
// The raw query should not contain the "LIMIT" and "OFFSET" clauses, they will be added automatically.
// The count query should return a single number (`COUNT(*)` for example).
func (p *Paginator) Raw(query string, vars []interface{}, countQuery string, countVars []interface{}) *Paginator {
p.rawQuery = query
p.rawQueryVars = vars
p.rawCountQuery = countQuery
p.rawCountQueryVars = countVars
return p
}
// UpdatePageInfo executes count request to calculate the `Total` and `MaxPage`.
func (p *Paginator) UpdatePageInfo() {
count := int64(0)
db := p.DB.Session(&gorm.Session{})
prevPreloads := db.Statement.Preloads
if len(prevPreloads) > 0 {
db.Statement.Preloads = map[string][]interface{}{}
defer func() {
db.Statement.Preloads = prevPreloads
}()
}
var err error
if p.rawCountQuery != "" {
err = db.Raw(p.rawCountQuery, p.rawCountQueryVars...).Scan(&count).Error
} else {
err = db.Model(p.Records).Count(&count).Error
}
if err != nil {
panic(err)
}
p.Total = count
p.MaxPage = int64(math.Ceil(float64(count) / float64(p.PageSize)))
if p.MaxPage == 0 {
p.MaxPage = 1
}
p.loadedPageInfo = true
}
// Find requests page information (total records and max page) and
// executes the transaction. The Paginate struct is updated automatically, as
// well as the destination slice given in NewPaginator().
func (p *Paginator) Find() *gorm.DB {
if !p.loadedPageInfo {
p.UpdatePageInfo()
}
if p.rawQuery != "" {
return p.rawStatement().Scan(p.Records)
}
return p.DB.Scopes(paginateScope(p.CurrentPage, p.PageSize)).Find(p.Records)
}
func (p *Paginator) rawStatement() *gorm.DB {
offset := (p.CurrentPage - 1) * p.PageSize
db := p.DB.Raw(p.rawQuery, p.rawQueryVars...)
db.Statement.SQL.WriteString(" ")
pageSize := p.PageSize
clause.Limit{Limit: &pageSize, Offset: offset}.Build(db.Statement)
return db
}