Skip to content

Instantly share code, notes, and snippets.

@cemremengu
Created October 12, 2022 07:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cemremengu/f7e66a61fe3dfaf6dcdb443dff1df1f4 to your computer and use it in GitHub Desktop.
Save cemremengu/f7e66a61fe3dfaf6dcdb443dff1df1f4 to your computer and use it in GitHub Desktop.
paginator.go
package paginator
import (
"context"
"database/sql"
"errors"
"github.com/uptrace/bun"
"math"
)
// TODO: make sort a list
type Pagination struct {
Page int `json:"page"`
Size int `json:"size"`
Sort Sort `json:"sort"`
}
type Sort struct {
Key string `json:"key"`
Order string `json:"order"`
}
func (s *Sort) String() string {
return s.Key + " " + s.Order
}
func (s *Sort) Valid() bool {
return s.Key != "" && s.Order != ""
}
type Paginator[T any] struct {
Params Pagination
Query *bun.SelectQuery
}
type Page[T any] struct {
Content []T `json:"content"`
TotalElements int `json:"totalElements"`
TotalPages int `json:"totalPages"`
Number int `json:"number"`
Size int `json:"size"`
NumberOfElements int `json:"numberOfElements"`
}
// Find applies pagination with given parameters
// See https://github.com/dmitryburov/gorm-paginator
// https://github.com/hellokaton/gorm-paginator
func (p *Paginator[T]) Find() (Page[T], error) {
var (
q = p.Query
done = make(chan int, 1)
defPage = 1
defLimit = 20
count int
offset int
result []T
)
// get all counts
go getCounts(q, done)
// limit
if p.Params.Size == 0 {
p.Params.Size = defLimit
}
// page
if p.Params.Page < 1 {
p.Params.Page = defPage
} else if p.Params.Page > 1 {
offset = (p.Params.Page - 1) * p.Params.Size
}
// sort
//if len(p.Paging.OrderBy) > 0 {
// for _, o := range p.Paging.OrderBy {
// db = db.Order(o)
// }
//} else {
// str := "id desc"
// p.Paging.OrderBy = append(p.Paging.OrderBy, str)
//}
if p.Params.Sort.Valid() {
q = q.Order(p.Params.Sort.String())
}
err := q.Limit(p.Params.Size).Offset(offset).Scan(context.TODO(), &result)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return Page[T]{}, err
}
count = <-done
// total pages
total := int(math.Ceil(float64(count) / float64(p.Params.Size)))
// construct pagination
page := Page[T]{
Content: result,
TotalElements: count,
TotalPages: total,
Number: p.Params.Page,
Size: p.Params.Size,
NumberOfElements: len(result),
}
//// prev page
//if p.Page > 1 {
// paginator.PrevPage = p.Paging.Page - 1
//}
//// next page
//if p.Page != paginator.TotalPage {
// paginator.NextPage = p.Paging.Page + 1
//}
return page, nil
}
func getCounts(q *bun.SelectQuery, done chan int) {
c, _ := q.Count(context.TODO())
done <- c
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment