Pagination
Pagination
Section titled “Pagination”// Full pagination (with total count)result, err := session. Where("status", "active"). Paginate(ctx, pie.PaginateParams{ Page: 1, PageSize: 10, })// result.Total, result.TotalPages, result.HasNext, result.HasPrev
// Simple pagination (no total count, faster)simpleResult, err := session. PaginateSimple(ctx, pie.PaginateParams{ Page: 1, PageSize: 10, })Pagination Types
Section titled “Pagination Types”Full Pagination
Section titled “Full Pagination”// Full pagination with total countresult, err := session. Where("status", "active"). OrderBy("created_at"). Paginate(ctx, pie.PaginateParams{ Page: 1, PageSize: 10, })
if err != nil { return err}
// Access pagination metadatafmt.Printf("Total items: %d\n", result.Total)fmt.Printf("Total pages: %d\n", result.TotalPages)fmt.Printf("Current page: %d\n", result.Page)fmt.Printf("Page size: %d\n", result.PageSize)fmt.Printf("Has next: %t\n", result.HasNext)fmt.Printf("Has prev: %t\n", result.HasPrev)
// Access datafor _, user := range result.Data { fmt.Printf("User: %s\n", user.Name)}Simple Pagination
Section titled “Simple Pagination”// Simple pagination without total count (faster)result, err := session. Where("status", "active"). OrderBy("created_at"). PaginateSimple(ctx, pie.PaginateParams{ Page: 1, PageSize: 10, })
if err != nil { return err}
// Access datafor _, user := range result.Data { fmt.Printf("User: %s\n", user.Name)}Advanced Pagination
Section titled “Advanced Pagination”Cursor-based Pagination
Section titled “Cursor-based Pagination”// Cursor-based pagination for large datasetsresult, err := session. Where("status", "active"). OrderBy("created_at"). PaginateCursor(ctx, pie.CursorPaginateParams{ Cursor: lastCursor, // From previous page PageSize: 10, })
if err != nil { return err}
// Get next cursor for next pagenextCursor := result.NextCursorOffset-style Pagination
Section titled “Offset-style Pagination”You can express a traditional offset/limit request by mapping it to the existing
Paginate (or PaginateSimple) helpers. For example, to skip the first 20
records and return 10 results per page you would set Page to
offset/pageSize + 1 and PageSize to the desired limit:
offset := 20pageSize := 10
result, err := session. Where("status", "active"). OrderBy("created_at"). Paginate(ctx, pie.PaginateParams{ Page: offset/pageSize + 1, PageSize: pageSize, })If you do not need the total count you can call PaginateSimple with the same
parameters. For real cursor-based pagination support, prefer
PaginateCursor instead of relying on offsets.
Real-world Examples
Section titled “Real-world Examples”API Pagination Handler
Section titled “API Pagination Handler”type PaginationRequest struct { Page int `json:"page" validate:"min=1"` PageSize int `json:"page_size" validate:"min=1,max=100"` SortBy string `json:"sort_by"` SortDir string `json:"sort_dir" validate:"oneof=asc desc"`}
type PaginationResponse struct { Data []User `json:"data"` Total int64 `json:"total"` Page int `json:"page"` PageSize int `json:"page_size"` TotalPages int `json:"total_pages"` HasNext bool `json:"has_next"` HasPrev bool `json:"has_prev"`}
func GetUsers(ctx context.Context, req PaginationRequest) (*PaginationResponse, error) { session := pie.Table[User](engine)
// Build query query := session.Where("status", "active")
// Apply sorting if req.SortBy != "" { if req.SortDir == "desc" { query = query.OrderByDesc(req.SortBy) } else { query = query.OrderBy(req.SortBy) } }
// Apply pagination result, err := query.Paginate(ctx, pie.PaginateParams{ Page: req.Page, PageSize: req.PageSize, })
if err != nil { return nil, err }
return &PaginationResponse{ Data: result.Data, Total: result.Total, Page: result.Page, PageSize: result.PageSize, TotalPages: result.TotalPages, HasNext: result.HasNext, HasPrev: result.HasPrev, }, nil}Search with Pagination
Section titled “Search with Pagination”type SearchRequest struct { Query string `json:"query"` Page int `json:"page"` PageSize int `json:"page_size"` SortBy string `json:"sort_by"`}
func SearchUsers(ctx context.Context, req SearchRequest) (*PaginationResponse, error) { session := pie.Table[User](engine)
// Build search query query := session.Where("status", "active")
if req.Query != "" { query = query.Where("name", pie.Like("name", "%"+req.Query+"%")) }
// Apply sorting if req.SortBy != "" { query = query.OrderBy(req.SortBy) }
// Apply pagination result, err := query.Paginate(ctx, pie.PaginateParams{ Page: req.Page, PageSize: req.PageSize, })
if err != nil { return nil, err }
return &PaginationResponse{ Data: result.Data, Total: result.Total, Page: result.Page, PageSize: result.PageSize, TotalPages: result.TotalPages, HasNext: result.HasNext, HasPrev: result.HasPrev, }, nil}Performance Optimization
Section titled “Performance Optimization”Use Indexes
Section titled “Use Indexes”// Ensure pagination fields are indexedfunc createPaginationIndexes() error { indexes := pie.MustIndexes(engine)
// Index for sorting err := indexes.CreateIndex(ctx, "users", bson.D{ {"status", 1}, {"created_at", -1}, }) if err != nil { return err }
// Index for search err = indexes.CreateIndex(ctx, "users", bson.D{ {"status", 1}, {"name", "text"}, })
return err}Caching
Section titled “Caching”// Cache pagination resultsfunc GetUsersCached(ctx context.Context, page, pageSize int) (*PaginationResponse, error) { cacheKey := fmt.Sprintf("users_page_%d_size_%d", page, pageSize)
// Try cache first var cached PaginationResponse if err := cache.Get(cacheKey, &cached); err == nil { return &cached, nil }
// Query database result, err := getUsersFromDB(ctx, page, pageSize) if err != nil { return nil, err }
// Cache result cache.Set(cacheKey, result, 5*time.Minute)
return result, nil}Best Practices
Section titled “Best Practices”1. Set Reasonable Page Sizes
Section titled “1. Set Reasonable Page Sizes”// Good: Reasonable page sizeconst ( DefaultPageSize = 20 MaxPageSize = 100)
func validatePageSize(pageSize int) int { if pageSize <= 0 { return DefaultPageSize } if pageSize > MaxPageSize { return MaxPageSize } return pageSize}2. Use Cursor Pagination for Large Datasets
Section titled “2. Use Cursor Pagination for Large Datasets”// For large datasets, use cursor-based paginationfunc GetLargeDataset(ctx context.Context, cursor string, pageSize int) (*CursorResult, error) { session := pie.Table[User](engine)
result, err := session. Where("status", "active"). OrderBy("created_at"). PaginateCursor(ctx, pie.CursorPaginateParams{ Cursor: cursor, PageSize: pageSize, })
return result, err}3. Handle Edge Cases
Section titled “3. Handle Edge Cases”func GetUsersSafe(ctx context.Context, page, pageSize int) (*PaginationResponse, error) { // Validate input if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 }
session := pie.Table[User](engine)
result, err := session. Where("status", "active"). Paginate(ctx, pie.PaginateParams{ Page: page, PageSize: pageSize, })
if err != nil { return nil, err }
// Handle empty results if len(result.Data) == 0 { return &PaginationResponse{ Data: []User{}, Total: 0, Page: page, PageSize: pageSize, TotalPages: 0, HasNext: false, HasPrev: false, }, nil }
return &PaginationResponse{ Data: result.Data, Total: result.Total, Page: result.Page, PageSize: result.PageSize, TotalPages: result.TotalPages, HasNext: result.HasNext, HasPrev: result.HasPrev, }, nil}Next Steps
Section titled “Next Steps”- Cursor Operations - Use cursors for large datasets
- Query Builder - Learn about query methods
- Transactions - Use transactions
- Performance - Learn performance optimization