pagination optimization

This commit is contained in:
Mazyar
2026-05-14 14:18:34 +03:30
parent eaa681814d
commit f08982afb5
3 changed files with 29 additions and 8 deletions
+9
View File
@@ -40,6 +40,14 @@ func collectionName() string {
return "tenders" return "tenders"
} }
// tenderSearchListProjection excludes large fields not needed for list/search API payloads.
func tenderSearchListProjection() bson.M {
return bson.M{
"content_xml": 0,
"document_summaries": 0,
}
}
// tenderRepository implements TenderRepository interface using MongoDB ORM // tenderRepository implements TenderRepository interface using MongoDB ORM
type tenderRepository struct { type tenderRepository struct {
ormRepo orm.Repository[Tender] ormRepo orm.Repository[Tender]
@@ -332,6 +340,7 @@ func (r *tenderRepository) Search(ctx context.Context, form *SearchForm, paginat
Limit(pagination.Limit). Limit(pagination.Limit).
Skip(pagination.Offset). Skip(pagination.Offset).
SortBy(sort, sortOrder). SortBy(sort, sortOrder).
Projection(tenderSearchListProjection()).
Build() Build()
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder) result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder)
+12 -5
View File
@@ -26,6 +26,8 @@ type Pagination struct {
Cursor string `json:"cursor"` // Base64 encoded cursor for cursor-based pagination Cursor string `json:"cursor"` // Base64 encoded cursor for cursor-based pagination
SortField string `json:"sortField"` // Field to sort by (default: "_id") SortField string `json:"sortField"` // Field to sort by (default: "_id")
SortOrder int `json:"sortOrder"` // Sort order: 1 for ascending, -1 for descending SortOrder int `json:"sortOrder"` // Sort order: 1 for ascending, -1 for descending
// Projection limits fields returned from Find (e.g. exclude large blobs on list endpoints).
Projection bson.M `json:"projection,omitempty"`
} }
// PaginatedResult represents a paginated result set // PaginatedResult represents a paginated result set
@@ -138,6 +140,10 @@ func (r *repository[T]) FindAll(ctx context.Context, filter bson.M, pagination P
SetSort(sort). SetSort(sort).
SetLimit(int64(pagination.Limit + 1)) // +1 to check if there are more documents SetLimit(int64(pagination.Limit + 1)) // +1 to check if there are more documents
if len(pagination.Projection) > 0 {
findOptions.SetProjection(pagination.Projection)
}
// Handle cursor-based pagination // Handle cursor-based pagination
if pagination.Cursor != "" { if pagination.Cursor != "" {
cursorFilter, err := r.buildCursorFilter(pagination.Cursor, pagination.SortField, pagination.SortOrder) cursorFilter, err := r.buildCursorFilter(pagination.Cursor, pagination.SortField, pagination.SortOrder)
@@ -181,13 +187,14 @@ func (r *repository[T]) FindAll(ctx context.Context, filter bson.M, pagination P
items = items[:pagination.Limit] // Remove the extra item items = items[:pagination.Limit] // Remove the extra item
} }
// Get total count // Get total count for pagination metadata
totalCount, err := r.Count(ctx, filter) var totalCount int64
if err != nil { var countErr error
totalCount, countErr = r.Count(ctx, filter)
if countErr != nil {
r.logger.Error("Failed to get total count", map[string]interface{}{ r.logger.Error("Failed to get total count", map[string]interface{}{
"error": err.Error(), "error": countErr.Error(),
}) })
// Don't fail the entire query, just set count to 0
totalCount = 0 totalCount = 0
} }
+8 -3
View File
@@ -7,8 +7,8 @@ import (
// Index represents a MongoDB index // Index represents a MongoDB index
type Index struct { type Index struct {
Name string `json:"name"` Name string `json:"name"`
Keys bson.D `json:"keys"` Keys bson.D `json:"keys"`
Options *options.IndexOptionsBuilder `json:"options"` Options *options.IndexOptionsBuilder `json:"options"`
} }
@@ -33,7 +33,6 @@ func (i *Index) WithSparse(sparse bool) *Index {
return i return i
} }
// WithExpireAfterSeconds sets the TTL for the index // WithExpireAfterSeconds sets the TTL for the index
func (i *Index) WithExpireAfterSeconds(seconds int32) *Index { func (i *Index) WithExpireAfterSeconds(seconds int32) *Index {
i.Options.SetExpireAfterSeconds(seconds) i.Options.SetExpireAfterSeconds(seconds)
@@ -294,6 +293,12 @@ func (pb *PaginationBuilder) SortDesc(field string) *PaginationBuilder {
return pb.SortBy(field, "desc") return pb.SortBy(field, "desc")
} }
// Projection sets optional MongoDB field projection for Find (e.g. exclude large fields).
func (pb *PaginationBuilder) Projection(projection bson.M) *PaginationBuilder {
pb.pagination.Projection = projection
return pb
}
// Build returns the final pagination options // Build returns the final pagination options
func (pb *PaginationBuilder) Build() Pagination { func (pb *PaginationBuilder) Build() Pagination {
return pb.pagination return pb.pagination