Skip to content

Commit 3f26fe2

Browse files
lunnywolfogresilverwind
authored
Use db.ListOptions directly instead of Paginator interface to make it easier to use and fix performance of /pulls and /issues (#29990)
This PR uses `db.ListOptions` instead of `Paginor` to make the code simpler. And it also fixed the performance problem when viewing /pulls or /issues. Before the counting in fact will also do the search. --------- Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: silverwind <me@silverwind.io>
1 parent ec3d467 commit 3f26fe2

File tree

8 files changed

+49
-34
lines changed

8 files changed

+49
-34
lines changed

models/issues/issue_search.go

+5-17
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121

2222
// IssuesOptions represents options of an issue.
2323
type IssuesOptions struct { //nolint
24-
db.Paginator
24+
Paginator *db.ListOptions
2525
RepoIDs []int64 // overwrites RepoCond if the length is not 0
2626
AllPublic bool // include also all public repositories
2727
RepoCond builder.Cond
@@ -104,23 +104,11 @@ func applyLimit(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
104104
return sess
105105
}
106106

107-
// Warning: Do not use GetSkipTake() for *db.ListOptions
108-
// Its implementation could reset the page size with setting.API.MaxResponseItems
109-
if listOptions, ok := opts.Paginator.(*db.ListOptions); ok {
110-
if listOptions.Page >= 0 && listOptions.PageSize > 0 {
111-
var start int
112-
if listOptions.Page == 0 {
113-
start = 0
114-
} else {
115-
start = (listOptions.Page - 1) * listOptions.PageSize
116-
}
117-
sess.Limit(listOptions.PageSize, start)
118-
}
119-
return sess
107+
start := 0
108+
if opts.Paginator.Page > 1 {
109+
start = (opts.Paginator.Page - 1) * opts.Paginator.PageSize
120110
}
121-
122-
start, limit := opts.Paginator.GetSkipTake()
123-
sess.Limit(limit, start)
111+
sess.Limit(opts.Paginator.PageSize, start)
124112

125113
return sess
126114
}

models/issues/issue_stats.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,17 @@ func CountIssuesByRepo(ctx context.Context, opts *IssuesOptions) (map[int64]int6
6868
}
6969

7070
// CountIssues number return of issues by given conditions.
71-
func CountIssues(ctx context.Context, opts *IssuesOptions) (int64, error) {
71+
func CountIssues(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Cond) (int64, error) {
7272
sess := db.GetEngine(ctx).
7373
Select("COUNT(issue.id) AS count").
7474
Table("issue").
7575
Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
7676
applyConditions(sess, opts)
7777

78+
for _, cond := range otherConds {
79+
sess.And(cond)
80+
}
81+
7882
return sess.Count()
7983
}
8084

modules/indexer/internal/paginator.go

+7-14
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
// ParsePaginator parses a db.Paginator into a skip and limit
13-
func ParsePaginator(paginator db.Paginator, max ...int) (int, int) {
13+
func ParsePaginator(paginator *db.ListOptions, max ...int) (int, int) {
1414
// Use a very large number to indicate no limit
1515
unlimited := math.MaxInt32
1616
if len(max) > 0 {
@@ -19,22 +19,15 @@ func ParsePaginator(paginator db.Paginator, max ...int) (int, int) {
1919
}
2020

2121
if paginator == nil || paginator.IsListAll() {
22+
// It shouldn't happen. In actual usage scenarios, there should not be requests to search all.
23+
// But if it does happen, respect it and return "unlimited".
24+
// And it's also useful for testing.
2225
return 0, unlimited
2326
}
2427

25-
// Warning: Do not use GetSkipTake() for *db.ListOptions
26-
// Its implementation could reset the page size with setting.API.MaxResponseItems
27-
if listOptions, ok := paginator.(*db.ListOptions); ok {
28-
if listOptions.Page >= 0 && listOptions.PageSize > 0 {
29-
var start int
30-
if listOptions.Page == 0 {
31-
start = 0
32-
} else {
33-
start = (listOptions.Page - 1) * listOptions.PageSize
34-
}
35-
return start, listOptions.PageSize
36-
}
37-
return 0, unlimited
28+
if paginator.PageSize == 0 {
29+
// Do not return any results when searching, it's used to get the total count only.
30+
return 0, 0
3831
}
3932

4033
return paginator.GetSkipTake()

modules/indexer/issues/db/db.go

+11
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
7878
return nil, err
7979
}
8080

81+
// If pagesize == 0, return total count only. It's a special case for search count.
82+
if options.Paginator != nil && options.Paginator.PageSize == 0 {
83+
total, err := issue_model.CountIssues(ctx, opt, cond)
84+
if err != nil {
85+
return nil, err
86+
}
87+
return &internal.SearchResult{
88+
Total: total,
89+
}, nil
90+
}
91+
8192
ids, total, err := issue_model.IssueIDs(ctx, opt, cond)
8293
if err != nil {
8394
return nil, err

modules/indexer/issues/indexer.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, err
308308

309309
// CountIssues counts issues by options. It is a shortcut of SearchIssues(ctx, opts) but only returns the total count.
310310
func CountIssues(ctx context.Context, opts *SearchOptions) (int64, error) {
311-
opts = opts.Copy(func(options *SearchOptions) { opts.Paginator = &db_model.ListOptions{PageSize: 0} })
311+
opts = opts.Copy(func(options *SearchOptions) { options.Paginator = &db_model.ListOptions{PageSize: 0} })
312312

313313
_, total, err := SearchIssues(ctx, opts)
314314
return total, err

modules/indexer/issues/internal/model.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ type SearchOptions struct {
106106
UpdatedAfterUnix optional.Option[int64]
107107
UpdatedBeforeUnix optional.Option[int64]
108108

109-
db.Paginator
109+
Paginator *db.ListOptions
110110

111111
SortBy SortBy // sort by field
112112
}

modules/indexer/issues/internal/tests/tests.go

+7
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) {
7777
assert.Equal(t, c.ExpectedIDs, ids)
7878
assert.Equal(t, c.ExpectedTotal, result.Total)
7979
}
80+
81+
// test counting
82+
c.SearchOptions.Paginator = &db.ListOptions{PageSize: 0}
83+
countResult, err := indexer.Search(context.Background(), c.SearchOptions)
84+
require.NoError(t, err)
85+
assert.Empty(t, countResult.Hits)
86+
assert.Equal(t, result.Total, countResult.Total)
8087
})
8188
}
8289
}

modules/indexer/issues/meilisearch/meilisearch.go

+12
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,14 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
218218

219219
skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits)
220220

221+
counting := limit == 0
222+
if counting {
223+
// If set limit to 0, it will be 20 by default, and -1 is not allowed.
224+
// See https://www.meilisearch.com/docs/reference/api/search#limit
225+
// So set limit to 1 to make the cost as low as possible, then clear the result before returning.
226+
limit = 1
227+
}
228+
221229
keyword := options.Keyword
222230
if !options.IsFuzzyKeyword {
223231
// to make it non fuzzy ("typo tolerance" in meilisearch terms), we have to quote the keyword(s)
@@ -236,6 +244,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
236244
return nil, err
237245
}
238246

247+
if counting {
248+
searchRes.Hits = nil
249+
}
250+
239251
hits, err := convertHits(searchRes)
240252
if err != nil {
241253
return nil, err

0 commit comments

Comments
 (0)