diff --git a/modules/indexer/issues/db/db.go b/modules/indexer/issues/db/db.go index 05ec548435f..6c9cfcf6708 100644 --- a/modules/indexer/issues/db/db.go +++ b/modules/indexer/issues/db/db.go @@ -71,6 +71,12 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( )), ), ) + + if options.IsKeywordNumeric() { + cond = cond.Or( + builder.Eq{"`index`": options.Keyword}, + ) + } } opt, err := ToDBOptions(ctx, options) diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 1cb86feb82c..c82dc0867ea 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -283,9 +283,9 @@ const ( func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, error) { indexer := *globalIndexer.Load() - if opts.Keyword == "" { + if opts.Keyword == "" || opts.IsKeywordNumeric() { // This is a conservative shortcut. - // If the keyword is empty, db has better (at least not worse) performance to filter issues. + // If the keyword is empty or an integer, db has better (at least not worse) performance to filter issues. // When the keyword is empty, it tends to listing rather than searching issues. // So if the user creates an issue and list issues immediately, the issue may not be listed because the indexer needs time to index the issue. // Even worse, the external indexer like elastic search may not be available for a while, diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index e426229f78e..4986d8d2943 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -31,6 +31,7 @@ func TestDBSearchIssues(t *testing.T) { InitIssueIndexer(true) t.Run("search issues with keyword", searchIssueWithKeyword) + t.Run("search issues by index", searchIssueByIndex) t.Run("search issues in repo", searchIssueInRepo) t.Run("search issues by ID", searchIssueByID) t.Run("search issues is pr", searchIssueIsPull) @@ -87,6 +88,43 @@ func searchIssueWithKeyword(t *testing.T) { } } +func searchIssueByIndex(t *testing.T) { + tests := []struct { + opts SearchOptions + expectedIDs []int64 + }{ + { + SearchOptions{ + Keyword: "1000", + RepoIDs: []int64{1}, + }, + []int64{}, + }, + { + SearchOptions{ + Keyword: "2", + RepoIDs: []int64{1, 2, 3, 32}, + }, + []int64{17, 12, 7, 2}, + }, + { + SearchOptions{ + Keyword: "1", + RepoIDs: []int64{58}, + }, + []int64{19}, + }, + } + + for _, test := range tests { + issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + if !assert.NoError(t, err) { + return + } + assert.Equal(t, test.expectedIDs, issueIDs) + } +} + func searchIssueInRepo(t *testing.T) { tests := []struct { opts SearchOptions diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 2dfee8b72e1..a43c6be0059 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -4,6 +4,8 @@ package internal import ( + "strconv" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" @@ -124,6 +126,12 @@ func (o *SearchOptions) Copy(edit ...func(options *SearchOptions)) *SearchOption return &v } +// used for optimized issue index based search +func (o *SearchOptions) IsKeywordNumeric() bool { + _, err := strconv.Atoi(o.Keyword) + return err == nil +} + type SortBy string const (