Skip to content

Rework suggestion backend #33538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 10, 2025
23 changes: 23 additions & 0 deletions models/issues/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,29 @@ func GetIssueByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
return issue, nil
}

func FindIssuesWithIndexPrefix(ctx context.Context, repoID, index int64, pageSize int) ([]*Issue, error) {
issues := make([]*Issue, 0, pageSize)
var cond string
switch {
case setting.Database.Type.IsSQLite3():
cond = "CAST(`index` AS TEXT) LIKE ?"
case setting.Database.Type.IsMySQL():
cond = "CAST(`index` AS CHAR) LIKE ?"
case setting.Database.Type.IsPostgreSQL():
cond = "index::TEXT LIKE ?"
case setting.Database.Type.IsMSSQL():
cond = "CAST([index] AS VARCHAR) LIKE ?"
}

err := db.GetEngine(ctx).Where("repo_id = ?", repoID).
And("`index` <> ?", index).
And(cond, fmt.Sprintf("%d%%", index)).
OrderBy("`index` ASC").
Limit(pageSize).
Find(&issues)
return issues, err
}

// GetIssueWithAttrsByIndex returns issue by index in a repository.
func GetIssueWithAttrsByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
issue, err := GetIssueByIndex(ctx, repoID, index)
Expand Down
99 changes: 71 additions & 28 deletions routers/web/repo/issue_suggestions.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package repo

import (
"net/http"
"sort"
"strconv"

"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
Expand All @@ -29,31 +31,78 @@ func IssueSuggestions(ctx *context.Context) {
isPull = optional.Some(false)
}

searchOpt := &issue_indexer.SearchOptions{
Paginator: &db.ListOptions{
Page: 0,
PageSize: 5,
},
Keyword: keyword,
RepoIDs: []int64{ctx.Repo.Repository.ID},
IsPull: isPull,
IsClosed: nil,
SortBy: issue_indexer.SortByUpdatedDesc,
indexKeyword, _ := strconv.ParseInt(keyword, 10, 64)
pageSize := 5
issues := make(issues_model.IssueList, 0, pageSize)
if indexKeyword > 0 {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, indexKeyword)
if err != nil && !issues_model.IsErrIssueNotExist(err) {
ctx.ServerError("GetIssueByIndex", err)
return
}
if issue != nil {
pageSize--
}

issues, err = issues_model.FindIssuesWithIndexPrefix(ctx, ctx.Repo.Repository.ID, indexKeyword, pageSize)
if err != nil {
ctx.ServerError("FindIssuesWithIndexPrefix", err)
return
}
pageSize -= len(issues)
if issue != nil {
issues = append([]*issues_model.Issue{issue}, issues...)
}
}

ids, _, err := issue_indexer.SearchIssues(ctx, searchOpt)
Copy link
Contributor

@wxiaoguang wxiaoguang Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue_indexer.SearchIssues does full-text search including issue content?

I am not sure whether it is good enough to only search title(name) by this PR's new approach.

image

Copy link
Member Author

@lunny lunny Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue_indexer.SearchIssues does full-text search including issue content?

I am not sure whether it is good enough to only search title(name) by this PR's new approach.

Looks like github did what I did but with a updated order.

image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Github will search both title and content but the title-matchted issue will be listed first.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Github will search both title and content but the title-matchted issue will be listed first.

Maybe GitHub uses search engine's ranking, not simply put something first.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can merge this first. Searching content is not usable from my side.

if err != nil {
ctx.ServerError("SearchIssues", err)
return
if pageSize > 0 {
searchOpt := &issue_indexer.SearchOptions{
Paginator: &db.ListOptions{
Page: 0,
PageSize: pageSize,
},
Keyword: keyword,
RepoIDs: []int64{ctx.Repo.Repository.ID},
IsPull: isPull,
IsClosed: nil,
SortBy: issue_indexer.SortByUpdatedDesc,
}

ids, _, err := issue_indexer.SearchIssues(ctx, searchOpt)
if err != nil {
ctx.ServerError("SearchIssues", err)
return
}

for i := 0; i < len(ids); i++ {
for _, issue := range issues {
if ids[i] == issue.ID {
ids = append(ids[:i], ids[i+1:]...)
i--
break
}
}
}

if len(ids) > 0 {
newIssues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
if err != nil {
ctx.ServerError("FindIssuesByIDs", err)
return
}
sort.Slice(newIssues, func(i, j int) bool {
return newIssues[i].Index > newIssues[j].Index
})
issues = append(issues, newIssues...)
}
}
issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
if err != nil {
ctx.ServerError("FindIssuesByIDs", err)

if err := issues.LoadPullRequests(ctx); err != nil {
ctx.ServerError("LoadPullRequests", err)
return
}

suggestions := make([]*structs.Issue, 0, len(issues))

for _, issue := range issues {
suggestion := &structs.Issue{
ID: issue.ID,
Expand All @@ -62,16 +111,10 @@ func IssueSuggestions(ctx *context.Context) {
State: issue.State(),
}

if issue.IsPull {
if err := issue.LoadPullRequest(ctx); err != nil {
ctx.ServerError("LoadPullRequest", err)
return
}
if issue.PullRequest != nil {
suggestion.PullRequest = &structs.PullRequestMeta{
HasMerged: issue.PullRequest.HasMerged,
IsWorkInProgress: issue.PullRequest.IsWorkInProgress(ctx),
}
if issue.IsPull && issue.PullRequest != nil {
suggestion.PullRequest = &structs.PullRequestMeta{
HasMerged: issue.PullRequest.HasMerged,
IsWorkInProgress: issue.PullRequest.IsWorkInProgress(ctx),
}
}

Expand Down