hybrid pagination
This commit is contained in:
+24
-13
@@ -177,11 +177,15 @@ func (h *TenderHandler) Search(c echo.Context) error {
|
||||
return response.ValidationError(c, "Validation failed", err.Error())
|
||||
}
|
||||
|
||||
// Call service
|
||||
tenders, err := h.service.Search(c.Request().Context(), form, response.NewPagination(c))
|
||||
pagination, err := response.NewPagination(c)
|
||||
if err != nil {
|
||||
if isInvalidPaginationCursor(err) {
|
||||
return response.ValidationError(c, "Invalid pagination cursor", err.Error())
|
||||
return response.PaginationBadRequest(c, err)
|
||||
}
|
||||
|
||||
tenders, err := h.service.Search(c.Request().Context(), form, pagination)
|
||||
if err != nil {
|
||||
if response.IsListPaginationError(err) {
|
||||
return response.PaginationBadRequest(c, err)
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to list tenders")
|
||||
}
|
||||
@@ -259,10 +263,15 @@ func (h *TenderHandler) GetPublicTenders(c echo.Context) error {
|
||||
}
|
||||
form.CustomerID = customerID
|
||||
|
||||
result, err := h.service.Search(c.Request().Context(), form, response.NewPagination(c))
|
||||
pagination, err := response.NewPagination(c)
|
||||
if err != nil {
|
||||
if isInvalidPaginationCursor(err) {
|
||||
return response.ValidationError(c, "Invalid pagination cursor", err.Error())
|
||||
return response.PaginationBadRequest(c, err)
|
||||
}
|
||||
|
||||
result, err := h.service.Search(c.Request().Context(), form, pagination)
|
||||
if err != nil {
|
||||
if response.IsListPaginationError(err) {
|
||||
return response.PaginationBadRequest(c, err)
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to retrieve tenders")
|
||||
}
|
||||
@@ -335,10 +344,15 @@ func (h *TenderHandler) RecommendTenders(c echo.Context) error {
|
||||
}
|
||||
form.CustomerID = customerID
|
||||
|
||||
result, err := h.service.Recommend(c.Request().Context(), form, response.NewPagination(c))
|
||||
pagination, err := response.NewPagination(c)
|
||||
if err != nil {
|
||||
if isInvalidPaginationCursor(err) {
|
||||
return response.ValidationError(c, "Invalid pagination cursor", err.Error())
|
||||
return response.PaginationBadRequest(c, err)
|
||||
}
|
||||
|
||||
result, err := h.service.Recommend(c.Request().Context(), form, pagination)
|
||||
if err != nil {
|
||||
if response.IsListPaginationError(err) {
|
||||
return response.PaginationBadRequest(c, err)
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to retrieve tenders")
|
||||
}
|
||||
@@ -664,6 +678,3 @@ func (h *TenderHandler) GetAllAISummaries(c echo.Context) error {
|
||||
return response.Success(c, summaries, "AI summaries retrieved successfully")
|
||||
}
|
||||
|
||||
func isInvalidPaginationCursor(err error) bool {
|
||||
return errors.Is(err, orm.ErrInvalidCursor)
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ func collectionName() string {
|
||||
return "tenders"
|
||||
}
|
||||
|
||||
// deepOffsetSkipCountThreshold skips CountDocuments when offset pagination goes this deep.
|
||||
const deepOffsetSkipCountThreshold = 1000
|
||||
|
||||
// SearchPageResult is the paginated outcome of a tender search/list query.
|
||||
type SearchPageResult struct {
|
||||
@@ -49,6 +47,7 @@ type SearchPageResult struct {
|
||||
TotalCount int64 // -1 when count was skipped
|
||||
NextCursor string
|
||||
HasMore bool
|
||||
PageOffset int
|
||||
}
|
||||
|
||||
// tenderSearchListProjection excludes large fields not needed for list/search API payloads.
|
||||
@@ -370,21 +369,20 @@ func (r *tenderRepository) Search(ctx context.Context, form *SearchForm, paginat
|
||||
sortOrder = pagination.SortOrder
|
||||
}
|
||||
|
||||
skipCount := pagination.Cursor != "" || pagination.Offset >= deepOffsetSkipCountThreshold
|
||||
|
||||
paginationBuilder := orm.NewPaginationBuilder().
|
||||
Limit(pagination.Limit).
|
||||
SortBy(sort, sortOrder).
|
||||
Projection(tenderSearchListProjection()).
|
||||
SkipCount(skipCount)
|
||||
|
||||
if pagination.Cursor != "" {
|
||||
paginationBuilder.Cursor(pagination.Cursor)
|
||||
} else {
|
||||
paginationBuilder.Skip(pagination.Offset)
|
||||
mongoPagination, err := orm.BuildListPagination(
|
||||
pagination.Limit,
|
||||
pagination.Offset,
|
||||
pagination.Cursor,
|
||||
sort,
|
||||
sortOrder,
|
||||
filter,
|
||||
orm.ListPaginationOptions{Projection: tenderSearchListProjection()},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder.Build())
|
||||
result, err := r.ormRepo.FindAll(ctx, filter, mongoPagination)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to search tenders", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
@@ -397,6 +395,7 @@ func (r *tenderRepository) Search(ctx context.Context, form *SearchForm, paginat
|
||||
TotalCount: result.TotalCount,
|
||||
NextCursor: result.NextCursor,
|
||||
HasMore: result.HasMore,
|
||||
PageOffset: result.PageOffset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -648,7 +648,7 @@ func (s *tenderService) Search(ctx context.Context, form *SearchForm, pagination
|
||||
|
||||
return &SearchResponse{
|
||||
Tenders: tenderResponses,
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore),
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore, page.PageOffset),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ func (s *tenderService) Search(ctx context.Context, form *SearchForm, pagination
|
||||
|
||||
return &SearchResponse{
|
||||
Tenders: tenderResponses,
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore),
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore, page.PageOffset),
|
||||
},
|
||||
nil
|
||||
}
|
||||
@@ -690,7 +690,7 @@ func (s *tenderService) Search(ctx context.Context, form *SearchForm, pagination
|
||||
|
||||
return &SearchResponse{
|
||||
Tenders: tenderResponses,
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore),
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore, page.PageOffset),
|
||||
},
|
||||
nil
|
||||
}
|
||||
@@ -757,7 +757,7 @@ func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, paginat
|
||||
|
||||
return &SearchResponse{
|
||||
Tenders: tenderResponses,
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore),
|
||||
Metadata: pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore, page.PageOffset),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user