# sqlx-record Pagination Skill Guide to pagination with Page and PageRequest. ## Triggers - "pagination", "paginate" - "page request", "page size" - "total pages", "has next" ## Overview `sqlx-record` provides built-in pagination support with the `Page` container and `PageRequest` options. ## PageRequest Create pagination options with 1-indexed page numbers: ```rust use sqlx_record::prelude::PageRequest; // Create request for page 1 with 20 items per page let request = PageRequest::new(1, 20); // First page shorthand let request = PageRequest::first(20); // Access offset/limit for manual queries request.offset() // 0 for page 1, 20 for page 2, etc. request.limit() // page_size ``` ## Page Paginated results container: ```rust use sqlx_record::prelude::Page; // Properties page.items // Vec - items for this page page.total_count // u64 - total records matching filters page.page // u32 - current page (1-indexed) page.page_size // u32 - items per page // Computed methods page.total_pages() // u32 - ceil(total_count / page_size) page.has_next() // bool - page < total_pages page.has_prev() // bool - page > 1 page.is_empty() // bool - items.is_empty() page.len() // usize - items.len() // Transformation page.map(|item| transform(item)) // Page page.into_items() // Vec page.iter() // impl Iterator ``` ## Entity Paginate Method Generated on all entities: ```rust pub async fn paginate( executor, filters: Vec, index: Option<&str>, // MySQL index hint order_by: Vec<(&str, bool)>, // (field, is_ascending) page_request: PageRequest ) -> Result, Error> ``` ## Usage Examples ### Basic Pagination ```rust use sqlx_record::prelude::*; // Get first page of 20 users let page = User::paginate( &pool, filters![], None, vec![("created_at", false)], // ORDER BY created_at DESC PageRequest::new(1, 20) ).await?; println!("Page {} of {}", page.page, page.total_pages()); println!("Showing {} of {} users", page.len(), page.total_count); for user in page.iter() { println!("{}: {}", user.id, user.name); } ``` ### With Filters ```rust // Active users only, page 3 let page = User::paginate( &pool, filters![("is_active", true)], None, vec![("name", true)], // ORDER BY name ASC PageRequest::new(3, 10) ).await?; ``` ### With Index Hint (MySQL) ```rust // Use specific index for performance let page = User::paginate( &pool, filters![("status", "active")], Some("idx_users_status"), // MySQL: USE INDEX(idx_users_status) vec![("created_at", false)], PageRequest::new(1, 50) ).await?; ``` ### Navigation Logic ```rust let page = User::paginate(&pool, filters![], None, vec![], PageRequest::new(current, 20)).await?; if page.has_prev() { println!("Previous: page {}", page.page - 1); } if page.has_next() { println!("Next: page {}", page.page + 1); } ``` ### Transform Results ```rust // Convert to DTOs let dto_page: Page = page.map(|user| UserDto::from(user)); // Or consume items let items: Vec = page.into_items(); ``` ## Comparison with Manual Pagination ```rust // Manual approach (still available) let offset = (page_num - 1) * page_size; let items = User::find_ordered_with_limit( &pool, filters, None, order_by, Some((offset, page_size)) ).await?; let total = User::count(&pool, filters.clone(), None).await?; // With paginate() - simpler let page = User::paginate(&pool, filters, None, order_by, PageRequest::new(page_num, page_size)).await?; ``` ## Notes - Page numbers are 1-indexed (page 1 is first page) - `paginate()` executes two queries: count + select - For very large tables, consider cursor-based pagination instead - Index hints only work on MySQL, ignored on Postgres/SQLite