From ab44d64068f020c22f43cc3bbff69ed142a590fa Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Fri, 18 Sep 2026 02:21:05 +0530 Subject: [PATCH] add cursor pagination and search sorting --- cmd/search.go | 40 +-- .../src/features/search/SearchPagination.vue | 81 ++++++ .../src/features/search/SearchResults.vue | 65 ++++- .../apps/main/src/views/search/SearchView.vue | 65 ++++- i18n/en-US.json | 2 +- internal/search/models/models.go | 18 +- internal/search/queries.sql | 5 +- internal/search/search.go | 269 +++++++++++++++--- internal/search/search_test.go | 171 +++++++++-- 9 files changed, 597 insertions(+), 119 deletions(-) create mode 100644 frontend/apps/main/src/features/search/SearchPagination.vue diff --git a/cmd/search.go b/cmd/search.go index 62a3b04a..6793dd99 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -21,6 +21,13 @@ const ( maxContactSearchLimit = 15 ) +type searchPageResults struct { + Results any `json:"results"` + PerPage int `json:"per_page"` + HasMore bool `json:"has_more"` + NextCursor string `json:"next_cursor"` +} + func handleSearchConversations(r *fastglue.Request) error { app, user, term, err := searchTerm(r) if err != nil { @@ -74,11 +81,16 @@ func handlePaginatedSearchConversations(r *fastglue.Request) error { if err != nil { return sendErrorEnvelope(r, err) } - results, total, err := app.search.Conversations(q, scope) + results, hasMore, nextCursor, err := app.search.Conversations(q, scope) if err != nil { return sendErrorEnvelope(r, err) } - return r.SendEnvelope(pageResults(results, total, q)) + return r.SendEnvelope(searchPageResults{ + Results: results, + PerPage: q.PageSize, + HasMore: hasMore, + NextCursor: nextCursor, + }) } func handlePaginatedSearchMessages(r *fastglue.Request) error { @@ -90,11 +102,16 @@ func handlePaginatedSearchMessages(r *fastglue.Request) error { if err != nil { return sendErrorEnvelope(r, err) } - results, total, err := app.search.Messages(q, scope) + results, hasMore, nextCursor, err := app.search.Messages(q, scope) if err != nil { return sendErrorEnvelope(r, err) } - return r.SendEnvelope(pageResults(results, total, q)) + return r.SendEnvelope(searchPageResults{ + Results: results, + PerPage: q.PageSize, + HasMore: hasMore, + NextCursor: nextCursor, + }) } func searchInputs(r *fastglue.Request) (*App, amodels.User, smodels.Query, error) { @@ -102,11 +119,12 @@ func searchInputs(r *fastglue.Request) (*App, amodels.User, smodels.Query, error if err != nil { return app, user, smodels.Query{}, err } - page, pageSize := getPagination(r) + _, pageSize := getPagination(r) query := searchmanager.NormalizeQuery(smodels.Query{ Term: term, Filters: string(r.RequestCtx.QueryArgs().Peek("filters")), - Page: page, + Cursor: string(r.RequestCtx.QueryArgs().Peek("cursor")), + Sort: smodels.Sort(r.RequestCtx.QueryArgs().Peek("sort")), PageSize: pageSize, }) return app, user, query, nil @@ -130,16 +148,6 @@ func searchLimit(r *fastglue.Request, max int) int { return limit } -func pageResults(results any, total int, q smodels.Query) envelope.PageResults { - return envelope.PageResults{ - Results: results, - Total: total, - PerPage: q.PageSize, - TotalPages: (total + q.PageSize - 1) / q.PageSize, - Page: q.Page, - } -} - func readScope(app *App, agentID int) (smodels.ReadScope, error) { agent, err := app.user.GetAgentCachedOrLoad(agentID) if err != nil { diff --git a/frontend/apps/main/src/features/search/SearchPagination.vue b/frontend/apps/main/src/features/search/SearchPagination.vue new file mode 100644 index 00000000..65813461 --- /dev/null +++ b/frontend/apps/main/src/features/search/SearchPagination.vue @@ -0,0 +1,81 @@ + + + diff --git a/frontend/apps/main/src/features/search/SearchResults.vue b/frontend/apps/main/src/features/search/SearchResults.vue index 02e899bc..58e202fd 100644 --- a/frontend/apps/main/src/features/search/SearchResults.vue +++ b/frontend/apps/main/src/features/search/SearchResults.vue @@ -1,13 +1,13 @@