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 SkipCount bool IncludeCount bool // when true, count even when using cursor pagination } // 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) } skipCount := opts.SkipCount if cursor != "" && !opts.IncludeCount { skipCount = true } if skipCount { pb.SkipCount(true) } 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 }