mirror of https://github.com/go-gitea/gitea.git
Allow searching issues by ID (#31479)
When you are entering a number in the issue search, you likely want the issue with the given ID (code internal concept: issue index). As such, when a number is detected, the issue with the corresponding ID will now be added to the results. Fixes #4479 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
416c36f303
commit
3571b7e3dd
|
@ -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)
|
opt, err := ToDBOptions(ctx, options)
|
||||||
|
|
|
@ -283,9 +283,9 @@ const (
|
||||||
func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, error) {
|
func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, error) {
|
||||||
indexer := *globalIndexer.Load()
|
indexer := *globalIndexer.Load()
|
||||||
|
|
||||||
if opts.Keyword == "" {
|
if opts.Keyword == "" || opts.IsKeywordNumeric() {
|
||||||
// This is a conservative shortcut.
|
// 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.
|
// 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.
|
// 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,
|
// Even worse, the external indexer like elastic search may not be available for a while,
|
||||||
|
|
|
@ -31,6 +31,7 @@ func TestDBSearchIssues(t *testing.T) {
|
||||||
InitIssueIndexer(true)
|
InitIssueIndexer(true)
|
||||||
|
|
||||||
t.Run("search issues with keyword", searchIssueWithKeyword)
|
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 in repo", searchIssueInRepo)
|
||||||
t.Run("search issues by ID", searchIssueByID)
|
t.Run("search issues by ID", searchIssueByID)
|
||||||
t.Run("search issues is pr", searchIssueIsPull)
|
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) {
|
func searchIssueInRepo(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
opts SearchOptions
|
opts SearchOptions
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
@ -124,6 +126,12 @@ func (o *SearchOptions) Copy(edit ...func(options *SearchOptions)) *SearchOption
|
||||||
return &v
|
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
|
type SortBy string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
Loading…
Reference in New Issue