Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.

Commit 45d6941

Browse files
authored
Merge pull request #42 from neuronlabs/develop
Fixed pagination issues. Added more tests on pagination and sorts.
2 parents 4f7ad14 + 17b8b9f commit 45d6941

File tree

6 files changed

+483
-22
lines changed

6 files changed

+483
-22
lines changed

query/pagination.go

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/neuronlabs/neuron-core/log"
1212
)
1313

14-
// Pagination constants
14+
// Pagination defined constants used for formatting the query.
1515
const (
1616
// ParamPage is a JSON API query parameter used as for pagination.
1717
ParamPage = "page"
@@ -39,6 +39,24 @@ const (
3939
PageNumberPagination
4040
)
4141

42+
// NewPaginationLimitOffset creates new Limit Offset pagination for given 'limit' and 'offset'.
43+
func NewPaginationLimitOffset(limit, offset int64) (*Pagination, error) {
44+
pagination := &Pagination{Type: LimitOffsetPagination, Size: limit, Offset: offset}
45+
if err := pagination.IsValid(); err != nil {
46+
return nil, err
47+
}
48+
return pagination, nil
49+
}
50+
51+
// NewPaginationNumberSize sets the pagination of the type PageNumberSize with the page 'number' and page 'size'.
52+
func NewPaginationNumberSize(number, size int64) (*Pagination, error) {
53+
pagination := &Pagination{Size: size, Offset: number, Type: PageNumberPagination}
54+
if err := pagination.IsValid(); err != nil {
55+
return nil, err
56+
}
57+
return pagination, nil
58+
}
59+
4260
// Pagination defines the query limits and offsets.
4361
// It defines the maximum size (Limit) as well as an offset at which
4462
// the query should start.
@@ -79,7 +97,7 @@ func (p *Pagination) First() (*Pagination, error) {
7997
return first, nil
8098
}
8199

82-
// FormatQuery formats the pagination for the url query.
100+
// FormatQuery formats the pagination for the url query with respect to JSONAPI specification.
83101
func (p *Pagination) FormatQuery(q ...url.Values) url.Values {
84102
var query url.Values
85103
if len(q) != 0 {
@@ -178,7 +196,7 @@ func (p *Pagination) IsValid() error {
178196
return p.checkValues()
179197
}
180198

181-
// IsZero checks if the pagination is already set.
199+
// IsZero checks if the pagination is zero valued.
182200
func (p *Pagination) IsZero() bool {
183201
return p.Size == 0 && p.Offset == 0
184202
}
@@ -199,14 +217,25 @@ func (p *Pagination) Last(total int64) (*Pagination, error) {
199217
var last *Pagination
200218
switch p.Type {
201219
case LimitOffsetPagination:
202-
offset := total - p.Size
203-
// in case when total size is lower then the pagination size set the offset to 0
204-
if offset < 0 {
205-
offset = 0
220+
var offset int64
221+
222+
// check if the last page is not partial
223+
if partialSize := p.Offset % p.Size; partialSize != 0 {
224+
lastFull := (total - partialSize) / p.Size
225+
offset = lastFull*p.Size + partialSize
226+
} else {
227+
// the last should be total/p.Size - (10-3)/2 = 3
228+
// 3 * size = 3 * 2 = 6
229+
offset = total - p.Size
230+
// in case when total size is lower then the pagination size set the offset to 0
231+
if offset < 0 {
232+
offset = 0
233+
}
206234
}
207235
if offset == p.Offset {
208236
return p, nil
209237
}
238+
// the offset should be total / p.Size
210239
last = &Pagination{Size: p.Size, Offset: offset, Type: LimitOffsetPagination}
211240
case PageNumberPagination:
212241
// divide total number of instances by the page size.
@@ -215,9 +244,9 @@ func (p *Pagination) Last(total int64) (*Pagination, error) {
215244
// pageSize - 10
216245
// computedPageNumber = 52/10 = 5
217246
pageNumber := total / p.Size
218-
if pageNumber == 0 {
219-
// in case when 'total' < pageSize the pageNumber = 1
220-
pageNumber = 1
247+
if total%p.Size != 0 || pageNumber == 0 {
248+
// total % p.Size = 52 % 10 = 2
249+
pageNumber++
221250
}
222251
last = &Pagination{Size: p.Size, Offset: pageNumber, Type: PageNumberPagination}
223252
default:
@@ -246,7 +275,7 @@ func (p *Pagination) Next(total int64) (*Pagination, error) {
246275
// in example:
247276
// total 52; p.Offset = 50; p.Size = 10
248277
// the next offset would be 60 which overflows possible total values.
249-
if offset > total {
278+
if offset >= total {
250279
return p, nil
251280
}
252281
next = &Pagination{Offset: offset, Size: p.Size, Type: LimitOffsetPagination}
@@ -256,10 +285,13 @@ func (p *Pagination) Next(total int64) (*Pagination, error) {
256285
// in example:
257286
// total: 52; pageNumber: 6; pageSize: 10;
258287
// nextTotal = pageNumber * pageSize = 60
259-
if p.Size*(p.Offset) > total {
288+
// 50 - (10 * 4+1) <= 0 ?
289+
//
290+
nextPageNumber := p.Offset + 1
291+
if total-(p.Size*(nextPageNumber)) <= 0 {
260292
return p, nil
261293
}
262-
next = &Pagination{Offset: p.Offset + 1, Size: p.Size, Type: PageNumberPagination}
294+
next = &Pagination{Offset: nextPageNumber, Size: p.Size, Type: PageNumberPagination}
263295
default:
264296
return nil, errors.NewDet(class.QueryPaginationType, "invalid pagination type")
265297
}
@@ -280,12 +312,12 @@ func (p *Pagination) Previous() (*Pagination, error) {
280312
if p.Offset == 0 {
281313
return p, nil
282314
}
283-
offset := p.Offset - p.Size
284-
if offset < 0 {
285-
return p, nil
315+
if p.Offset < p.Size {
316+
prev = &Pagination{Offset: 0, Size: p.Offset, Type: LimitOffsetPagination}
317+
return prev, nil
286318
}
287319
// keep the same size but change the offset
288-
prev = &Pagination{Offset: offset, Size: p.Size, Type: LimitOffsetPagination}
320+
prev = &Pagination{Offset: p.Offset - p.Size, Size: p.Size, Type: LimitOffsetPagination}
289321
case PageNumberPagination:
290322
if p.Offset <= 1 {
291323
return p, nil

0 commit comments

Comments
 (0)