hybrid pagination
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package mongo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultListLimit is the default page size for admin list endpoints.
|
||||
DefaultListLimit = 10
|
||||
// MaxListLimit is the maximum allowed page size.
|
||||
MaxListLimit = 100
|
||||
)
|
||||
|
||||
// ListPaginationOptions configures optional list pagination behavior.
|
||||
type ListPaginationOptions struct {
|
||||
Projection bson.M
|
||||
}
|
||||
|
||||
// BuildListPagination converts API pagination parameters into MongoDB pagination options.
|
||||
// filter is used to bind cursors to the active query filters via a hash.
|
||||
func BuildListPagination(
|
||||
limit, offset int,
|
||||
cursor, sortField, sortOrder string,
|
||||
filter bson.M,
|
||||
opts ListPaginationOptions,
|
||||
) (Pagination, error) {
|
||||
if limit <= 0 {
|
||||
limit = DefaultListLimit
|
||||
}
|
||||
if limit > MaxListLimit {
|
||||
return Pagination{}, fmt.Errorf("%w: limit must be between 1 and %d", ErrInvalidPagination, MaxListLimit)
|
||||
}
|
||||
|
||||
if sortField == "" {
|
||||
sortField = "created_at"
|
||||
}
|
||||
if sortOrder == "" {
|
||||
sortOrder = "desc"
|
||||
}
|
||||
|
||||
sortOrderInt := -1
|
||||
if sortOrder == "asc" {
|
||||
sortOrderInt = 1
|
||||
}
|
||||
|
||||
filtersHash := HashFilter(filter)
|
||||
|
||||
pb := NewPaginationBuilder().
|
||||
Limit(limit).
|
||||
SortBy(sortField, sortOrder)
|
||||
|
||||
if len(opts.Projection) > 0 {
|
||||
pb.Projection(opts.Projection)
|
||||
}
|
||||
|
||||
if cursor != "" {
|
||||
pageCursor, err := decodePageCursor(cursor)
|
||||
if err != nil {
|
||||
return Pagination{}, err
|
||||
}
|
||||
if !pageCursor.matches(sortField, sortOrderInt) {
|
||||
return Pagination{}, fmt.Errorf("%w: cursor does not match requested sort", ErrInvalidCursor)
|
||||
}
|
||||
if pageCursor.FiltersHash != filtersHash {
|
||||
return Pagination{}, fmt.Errorf("%w: cursor does not match current filters", ErrInvalidCursor)
|
||||
}
|
||||
pb.Cursor(cursor)
|
||||
} else {
|
||||
if offset < 0 {
|
||||
return Pagination{}, fmt.Errorf("%w: offset cannot be negative", ErrInvalidPagination)
|
||||
}
|
||||
pb.Skip(offset)
|
||||
}
|
||||
|
||||
return pb.Build(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user