pagination optimization
This commit is contained in:
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user