Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 49 additions & 17 deletions query/pagination.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/neuronlabs/neuron-core/log"
)

// Pagination constants
// Pagination defined constants used for formatting the query.
const (
// ParamPage is a JSON API query parameter used as for pagination.
ParamPage = "page"
Expand Down Expand Up @@ -39,6 +39,24 @@ const (
PageNumberPagination
)

// NewPaginationLimitOffset creates new Limit Offset pagination for given 'limit' and 'offset'.
func NewPaginationLimitOffset(limit, offset int64) (*Pagination, error) {
pagination := &Pagination{Type: LimitOffsetPagination, Size: limit, Offset: offset}
if err := pagination.IsValid(); err != nil {
return nil, err
}
return pagination, nil
}

// NewPaginationNumberSize sets the pagination of the type PageNumberSize with the page 'number' and page 'size'.
func NewPaginationNumberSize(number, size int64) (*Pagination, error) {
pagination := &Pagination{Size: size, Offset: number, Type: PageNumberPagination}
if err := pagination.IsValid(); err != nil {
return nil, err
}
return pagination, nil
}

// Pagination defines the query limits and offsets.
// It defines the maximum size (Limit) as well as an offset at which
// the query should start.
Expand Down Expand Up @@ -79,7 +97,7 @@ func (p *Pagination) First() (*Pagination, error) {
return first, nil
}

// FormatQuery formats the pagination for the url query.
// FormatQuery formats the pagination for the url query with respect to JSONAPI specification.
func (p *Pagination) FormatQuery(q ...url.Values) url.Values {
var query url.Values
if len(q) != 0 {
Expand Down Expand Up @@ -178,7 +196,7 @@ func (p *Pagination) IsValid() error {
return p.checkValues()
}

// IsZero checks if the pagination is already set.
// IsZero checks if the pagination is zero valued.
func (p *Pagination) IsZero() bool {
return p.Size == 0 && p.Offset == 0
}
Expand All @@ -199,14 +217,25 @@ func (p *Pagination) Last(total int64) (*Pagination, error) {
var last *Pagination
switch p.Type {
case LimitOffsetPagination:
offset := total - p.Size
// in case when total size is lower then the pagination size set the offset to 0
if offset < 0 {
offset = 0
var offset int64

// check if the last page is not partial
if partialSize := p.Offset % p.Size; partialSize != 0 {
lastFull := (total - partialSize) / p.Size
offset = lastFull*p.Size + partialSize
} else {
// the last should be total/p.Size - (10-3)/2 = 3
// 3 * size = 3 * 2 = 6
offset = total - p.Size
// in case when total size is lower then the pagination size set the offset to 0
if offset < 0 {
offset = 0
}
}
if offset == p.Offset {
return p, nil
}
// the offset should be total / p.Size
last = &Pagination{Size: p.Size, Offset: offset, Type: LimitOffsetPagination}
case PageNumberPagination:
// divide total number of instances by the page size.
Expand All @@ -215,9 +244,9 @@ func (p *Pagination) Last(total int64) (*Pagination, error) {
// pageSize - 10
// computedPageNumber = 52/10 = 5
pageNumber := total / p.Size
if pageNumber == 0 {
// in case when 'total' < pageSize the pageNumber = 1
pageNumber = 1
if total%p.Size != 0 || pageNumber == 0 {
// total % p.Size = 52 % 10 = 2
pageNumber++
}
last = &Pagination{Size: p.Size, Offset: pageNumber, Type: PageNumberPagination}
default:
Expand Down Expand Up @@ -246,7 +275,7 @@ func (p *Pagination) Next(total int64) (*Pagination, error) {
// in example:
// total 52; p.Offset = 50; p.Size = 10
// the next offset would be 60 which overflows possible total values.
if offset > total {
if offset >= total {
return p, nil
}
next = &Pagination{Offset: offset, Size: p.Size, Type: LimitOffsetPagination}
Expand All @@ -256,10 +285,13 @@ func (p *Pagination) Next(total int64) (*Pagination, error) {
// in example:
// total: 52; pageNumber: 6; pageSize: 10;
// nextTotal = pageNumber * pageSize = 60
if p.Size*(p.Offset) > total {
// 50 - (10 * 4+1) <= 0 ?
//
nextPageNumber := p.Offset + 1
if total-(p.Size*(nextPageNumber)) <= 0 {
return p, nil
}
next = &Pagination{Offset: p.Offset + 1, Size: p.Size, Type: PageNumberPagination}
next = &Pagination{Offset: nextPageNumber, Size: p.Size, Type: PageNumberPagination}
default:
return nil, errors.NewDet(class.QueryPaginationType, "invalid pagination type")
}
Expand All @@ -280,12 +312,12 @@ func (p *Pagination) Previous() (*Pagination, error) {
if p.Offset == 0 {
return p, nil
}
offset := p.Offset - p.Size
if offset < 0 {
return p, nil
if p.Offset < p.Size {
prev = &Pagination{Offset: 0, Size: p.Offset, Type: LimitOffsetPagination}
return prev, nil
}
// keep the same size but change the offset
prev = &Pagination{Offset: offset, Size: p.Size, Type: LimitOffsetPagination}
prev = &Pagination{Offset: p.Offset - p.Size, Size: p.Size, Type: LimitOffsetPagination}
case PageNumberPagination:
if p.Offset <= 1 {
return p, nil
Expand Down
Loading