From bbb162a385b84652ab394d0d32c28f4bde4f94f4 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 20 May 2023 22:22:54 +0800 Subject: [PATCH 01/10] Remove issue_ids conditions --- models/issues/issue_list.go | 10 ++++ models/issues/issue_search.go | 76 ++++++++++++++++++++-------- models/issues/issue_stats.go | 66 ++++-------------------- models/issues/issue_test.go | 26 +--------- routers/api/v1/repo/issue.go | 64 ++++++++++-------------- routers/web/org/projects.go | 5 +- routers/web/repo/issue.go | 93 +++++++++++----------------------- routers/web/repo/projects.go | 5 +- routers/web/user/home.go | 94 +++++++++-------------------------- 9 files changed, 155 insertions(+), 284 deletions(-) diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 6ddadd27ed442..916bb7ad315be 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -12,6 +12,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) @@ -603,3 +604,12 @@ func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*Rev return approvalCountMap, nil } + +func FindIssuesByIDs(ctx context.Context, ids []int64, isPull util.OptionalBool) (IssueList, error) { + sess := db.GetEngine(ctx).In("id", ids) + if !isPull.IsNone() { + sess.And("is_pull=?", isPull.IsTrue()) + } + var issues IssueList + return issues, sess.Find(&issues) +} diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 9fd13f09956af..07aa096fc86c4 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -40,15 +40,18 @@ type IssuesOptions struct { //nolint ExcludedLabelNames []string IncludeMilestones []string SortType string - IssueIDs []int64 - UpdatedAfterUnix int64 - UpdatedBeforeUnix int64 + // IssueIDs []int64 + UpdatedAfterUnix int64 + UpdatedBeforeUnix int64 // prioritize issues from this repo PriorityRepoID int64 IsArchived util.OptionalBool Org *organization.Organization // issues permission scope Team *organization.Team // issues permission scope User *user_model.User // issues permission scope + + Keyword string + DontSearchComment bool // whether to search in comments } // applySorts sort an issues-related session based on the provided @@ -111,6 +114,29 @@ func applyLimit(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { return sess } +func applyKeywordCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { + if opts.Keyword == "" { + return sess + } + + return sess.Where( + builder.Or( + db.BuildCaseInsensitiveLike("issue.name", opts.Keyword), + db.BuildCaseInsensitiveLike("issue.content", opts.Keyword), + builder.If(!opts.DontSearchComment, + builder.In("id", builder.Select("issue_id"). + From("comment"). + Where(builder.And( + builder.Expr("comment.issue_id = issue.id"), + builder.Eq{"type": CommentTypeComment}, + db.BuildCaseInsensitiveLike("content", opts.Keyword), + )), + ), + ), + ), + ) +} + func applyLabelsCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { if len(opts.LabelIDs) > 0 { if opts.LabelIDs[0] == 0 { @@ -157,20 +183,28 @@ func applyMilestoneCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Sess func applyRepoConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { if len(opts.RepoIDs) == 1 { - opts.RepoCond = builder.Eq{"issue.repo_id": opts.RepoIDs[0]} + sess.And(builder.Eq{"issue.repo_id": opts.RepoIDs[0]}) } else if len(opts.RepoIDs) > 1 { - opts.RepoCond = builder.In("issue.repo_id", opts.RepoIDs) - } - if opts.RepoCond != nil { + sess.And(builder.In("issue.repo_id", opts.RepoIDs)) + } else if opts.RepoCond != nil { sess.And(opts.RepoCond) } return sess } +func applyIsPullCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { + if !opts.IsPull.IsNone() { + sess.And("issue.is_pull=?", opts.IsPull.IsTrue()) + } + return sess +} + func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { - if len(opts.IssueIDs) > 0 { + /*if len(opts.IssueIDs) > 0 { sess.In("issue.id", opts.IssueIDs) - } + }*/ + + applyKeywordCondition(sess, opts) applyRepoConditions(sess, opts) @@ -178,11 +212,7 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { sess.And("issue.is_closed=?", opts.IsClosed.IsTrue()) } - if opts.AssigneeID > 0 { - applyAssigneeCondition(sess, opts.AssigneeID) - } else if opts.AssigneeID == db.NoConditionID { - sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)") - } + applyAssigneeCondition(sess, opts.AssigneeID) if opts.PosterID > 0 { applyPosterCondition(sess, opts.PosterID) @@ -228,12 +258,7 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { } } - switch opts.IsPull { - case util.OptionalBoolTrue: - sess.And("issue.is_pull=?", true) - case util.OptionalBoolFalse: - sess.And("issue.is_pull=?", false) - } + applyIsPullCondition(sess, opts) if opts.IsArchived != util.OptionalBoolNone { sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()}) @@ -323,8 +348,15 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati } func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session { - return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). - And("issue_assignees.assignee_id = ?", assigneeID) + switch assigneeID { + case 0: + return sess + case db.NoConditionID: + return sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)") + default: + return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", assigneeID) + } } func applyPosterCondition(sess *xorm.Session, posterID int64) *xorm.Session { diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index 9b9562ebdd795..dbf7d95ba7f9d 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -82,52 +82,14 @@ func CountIssues(ctx context.Context, opts *IssuesOptions) (int64, error) { // GetIssueStats returns issue statistic information by given conditions. func GetIssueStats(opts *IssuesOptions) (*IssueStats, error) { - if len(opts.IssueIDs) <= MaxQueryParameters { - return getIssueStatsChunk(opts, opts.IssueIDs) - } - - // If too long a list of IDs is provided, we get the statistics in - // smaller chunks and get accumulates. Note: this could potentially - // get us invalid results. The alternative is to insert the list of - // ids in a temporary table and join from them. - accum := &IssueStats{} - for i := 0; i < len(opts.IssueIDs); { - chunk := i + MaxQueryParameters - if chunk > len(opts.IssueIDs) { - chunk = len(opts.IssueIDs) - } - stats, err := getIssueStatsChunk(opts, opts.IssueIDs[i:chunk]) - if err != nil { - return nil, err - } - accum.OpenCount += stats.OpenCount - accum.ClosedCount += stats.ClosedCount - accum.YourRepositoriesCount += stats.YourRepositoriesCount - accum.AssignCount += stats.AssignCount - accum.CreateCount += stats.CreateCount - accum.OpenCount += stats.MentionCount - accum.ReviewRequestedCount += stats.ReviewRequestedCount - accum.ReviewedCount += stats.ReviewedCount - i = chunk - } - return accum, nil -} - -func getIssueStatsChunk(opts *IssuesOptions, issueIDs []int64) (*IssueStats, error) { stats := &IssueStats{} - countSession := func(opts *IssuesOptions, issueIDs []int64) *xorm.Session { + countSession := func(opts *IssuesOptions) *xorm.Session { sess := db.GetEngine(db.DefaultContext). Join("INNER", "repository", "`issue`.repo_id = `repository`.id") - if len(opts.RepoIDs) > 1 { - sess.In("issue.repo_id", opts.RepoIDs) - } else if len(opts.RepoIDs) == 1 { - sess.And("issue.repo_id = ?", opts.RepoIDs[0]) - } + applyRepoConditions(sess, opts) - if len(issueIDs) > 0 { - sess.In("issue.id", issueIDs) - } + applyKeywordCondition(sess, opts) applyLabelsCondition(sess, opts) @@ -138,11 +100,7 @@ func getIssueStatsChunk(opts *IssuesOptions, issueIDs []int64) (*IssueStats, err And("project_issue.project_id=?", opts.ProjectID) } - if opts.AssigneeID > 0 { - applyAssigneeCondition(sess, opts.AssigneeID) - } else if opts.AssigneeID == db.NoConditionID { - sess.Where("id NOT IN (SELECT issue_id FROM issue_assignees)") - } + applyAssigneeCondition(sess, opts.AssigneeID) if opts.PosterID > 0 { applyPosterCondition(sess, opts.PosterID) @@ -160,24 +118,19 @@ func getIssueStatsChunk(opts *IssuesOptions, issueIDs []int64) (*IssueStats, err applyReviewedCondition(sess, opts.ReviewedID) } - switch opts.IsPull { - case util.OptionalBoolTrue: - sess.And("issue.is_pull=?", true) - case util.OptionalBoolFalse: - sess.And("issue.is_pull=?", false) - } + applyIsPullCondition(sess, opts) return sess } var err error - stats.OpenCount, err = countSession(opts, issueIDs). + stats.OpenCount, err = countSession(opts). And("issue.is_closed = ?", false). Count(new(Issue)) if err != nil { return stats, err } - stats.ClosedCount, err = countSession(opts, issueIDs). + stats.ClosedCount, err = countSession(opts). And("issue.is_closed = ?", true). Count(new(Issue)) return stats, err @@ -202,9 +155,6 @@ func GetUserIssueStats(filterMode int, opts IssuesOptions) (*IssueStats, error) if len(opts.RepoIDs) > 0 { cond = cond.And(builder.In("issue.repo_id", opts.RepoIDs)) } - if len(opts.IssueIDs) > 0 { - cond = cond.And(builder.In("issue.id", opts.IssueIDs)) - } if opts.RepoCond != nil { cond = cond.And(opts.RepoCond) } @@ -222,6 +172,8 @@ func GetUserIssueStats(filterMode int, opts IssuesOptions) (*IssueStats, error) In("issue_label.label_id", opts.LabelIDs) } + applyKeywordCondition(s, &opts) + if opts.IsArchived != util.OptionalBoolNone { s.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()}) } diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 80699a57b4e86..944e6003c4098 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -283,21 +283,6 @@ func TestGetUserIssueStats(t *testing.T) { ClosedCount: 0, }, }, - { - issues_model.FilterModeCreate, - issues_model.IssuesOptions{ - User: unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}), - IssueIDs: []int64{1}, - IsPull: util.OptionalBoolFalse, - }, - issues_model.IssueStats{ - YourRepositoriesCount: 1, // 1 - AssignCount: 1, // 1 - CreateCount: 1, // 1 - OpenCount: 1, // 1 - ClosedCount: 0, - }, - }, { issues_model.FilterModeAll, issues_model.IssuesOptions{ @@ -494,18 +479,11 @@ func TestCorrectIssueStats(t *testing.T) { } wg.Wait() - // Now we will get all issueID's that match the "Bugs are nasty" query. - total, ids, err := issues_model.SearchIssueIDsByKeyword(context.TODO(), "Bugs are nasty", []int64{1}, issueAmount, 0) - - // Just to be sure. - assert.NoError(t, err) - assert.EqualValues(t, issueAmount, total) - // Now we will call the GetIssueStats with these IDs and if working, // get the correct stats back. issueStats, err := issues_model.GetIssueStats(&issues_model.IssuesOptions{ - RepoIDs: []int64{1}, - IssueIDs: ids, + RepoIDs: []int64{1}, + Keyword: "Bugs are nasty", }) // Now check the values. diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 95528d664d7b9..0eee826e3b029 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -242,7 +242,6 @@ func SearchIssues(ctx *context.APIContext) { }, RepoCond: repoCond, IsClosed: isClosed, - IssueIDs: issueIDs, IncludedLabelNames: includedLabelNames, IncludeMilestones: includedMilestones, SortType: "priorityrepo", @@ -250,6 +249,7 @@ func SearchIssues(ctx *context.APIContext) { IsPull: isPull, UpdatedBeforeUnix: before, UpdatedAfterUnix: since, + Keyword: keyword, } ctxUserID := int64(0) @@ -391,16 +391,8 @@ func ListIssues(ctx *context.APIContext) { if strings.IndexByte(keyword, 0) >= 0 { keyword = "" } - var issueIDs []int64 - var labelIDs []int64 - if len(keyword) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword) - if err != nil { - ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err) - return - } - } + var labelIDs []int64 if splitted := strings.Split(ctx.FormString("labels"), ","); len(splitted) > 0 { labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx.Repo.Repository.ID, splitted) if err != nil { @@ -467,34 +459,32 @@ func ListIssues(ctx *context.APIContext) { // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. - if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { - issuesOpt := &issues_model.IssuesOptions{ - ListOptions: listOptions, - RepoIDs: []int64{ctx.Repo.Repository.ID}, - IsClosed: isClosed, - IssueIDs: issueIDs, - LabelIDs: labelIDs, - MilestoneIDs: mileIDs, - IsPull: isPull, - UpdatedBeforeUnix: before, - UpdatedAfterUnix: since, - PosterID: createdByID, - AssigneeID: assignedByID, - MentionedID: mentionedByID, - } - - if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, "Issues", err) - return - } + issuesOpt := &issues_model.IssuesOptions{ + ListOptions: listOptions, + RepoIDs: []int64{ctx.Repo.Repository.ID}, + IsClosed: isClosed, + LabelIDs: labelIDs, + MilestoneIDs: mileIDs, + IsPull: isPull, + UpdatedBeforeUnix: before, + UpdatedAfterUnix: since, + PosterID: createdByID, + AssigneeID: assignedByID, + MentionedID: mentionedByID, + Keyword: keyword, + } + + if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, "Issues", err) + return + } - issuesOpt.ListOptions = db.ListOptions{ - Page: -1, - } - if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, "CountIssues", err) - return - } + issuesOpt.ListOptions = db.ListOptions{ + Page: -1, + } + if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, "CountIssues", err) + return } ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 32b60e995f2e2..48bc8f2923eb7 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -362,10 +362,7 @@ func ViewProject(ctx *context.Context) { } if len(referencedIds) > 0 { - if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ - IssueIDs: referencedIds, - IsPull: util.OptionalBoolTrue, - }); err == nil { + if linkedPrs, err := issues_model.FindIssuesByIDs(ctx, referencedIds, util.OptionalBoolTrue); err == nil { linkedPrsMap[issue.ID] = linkedPrs } } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 88d2a97a7ad9c..e50257d16341c 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -32,7 +32,6 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" - issue_indexer "code.gitea.io/gitea/modules/indexer/issues" issue_template "code.gitea.io/gitea/modules/issue/template" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -187,21 +186,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti keyword = "" } - var issueIDs []int64 - if len(keyword) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{repo.ID}, keyword) - if err != nil { - if issue_indexer.IsAvailable() { - ctx.ServerError("issueIndexer.Search", err) - return - } - ctx.Data["IssueIndexerUnavailable"] = true - } - if len(issueIDs) == 0 { - forceEmpty = true - } - } - var issueStats *issues_model.IssueStats if forceEmpty { issueStats = &issues_model.IssueStats{} @@ -217,7 +201,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti ReviewRequestedID: reviewRequestedID, ReviewedID: reviewedID, IsPull: isPullOption, - IssueIDs: issueIDs, + Keyword: keyword, }) if err != nil { ctx.ServerError("GetIssueStats", err) @@ -270,7 +254,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti IsPull: isPullOption, LabelIDs: labelIDs, SortType: sortType, - IssueIDs: issueIDs, + Keyword: keyword, }) if err != nil { ctx.ServerError("Issues", err) @@ -2408,7 +2392,6 @@ func SearchIssues(ctx *context.Context) { } repoCond := repo_model.SearchRepositoryCondition(opts) - repoIDs, _, err := repo_model.SearchRepositoryIDs(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "SearchRepositoryIDs", err.Error()) return @@ -2421,13 +2404,6 @@ func SearchIssues(ctx *context.Context) { if strings.IndexByte(keyword, 0) >= 0 { keyword = "" } - var issueIDs []int64 - if len(keyword) > 0 && len(repoIDs) > 0 { - if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword); err != nil { - ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err.Error()) - return - } - } var isPull util.OptionalBool switch ctx.FormString("type") { @@ -2464,7 +2440,7 @@ func SearchIssues(ctx *context.Context) { // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. - if len(keyword) == 0 || len(issueIDs) > 0 || len(includedLabelNames) > 0 || len(includedMilestones) > 0 { + if len(keyword) == 0 || len(includedLabelNames) > 0 || len(includedMilestones) > 0 { issuesOpt := &issues_model.IssuesOptions{ ListOptions: db.ListOptions{ Page: ctx.FormInt("page"), @@ -2472,7 +2448,6 @@ func SearchIssues(ctx *context.Context) { }, RepoCond: repoCond, IsClosed: isClosed, - IssueIDs: issueIDs, IncludedLabelNames: includedLabelNames, IncludeMilestones: includedMilestones, ProjectID: projectID, @@ -2481,6 +2456,7 @@ func SearchIssues(ctx *context.Context) { IsPull: isPull, UpdatedBeforeUnix: before, UpdatedAfterUnix: since, + Keyword: keyword, } ctxUserID := int64(0) @@ -2568,16 +2544,8 @@ func ListIssues(ctx *context.Context) { if strings.IndexByte(keyword, 0) >= 0 { keyword = "" } - var issueIDs []int64 - var labelIDs []int64 - if len(keyword) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword) - if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) - return - } - } + var labelIDs []int64 if splitted := strings.Split(ctx.FormString("labels"), ","); len(splitted) > 0 { labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx.Repo.Repository.ID, splitted) if err != nil { @@ -2649,35 +2617,30 @@ func ListIssues(ctx *context.Context) { // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. - if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { - issuesOpt := &issues_model.IssuesOptions{ - ListOptions: listOptions, - RepoIDs: []int64{ctx.Repo.Repository.ID}, - IsClosed: isClosed, - IssueIDs: issueIDs, - LabelIDs: labelIDs, - MilestoneIDs: mileIDs, - ProjectID: projectID, - IsPull: isPull, - UpdatedBeforeUnix: before, - UpdatedAfterUnix: since, - PosterID: createdByID, - AssigneeID: assignedByID, - MentionedID: mentionedByID, - } - - if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) - return - } + issuesOpt := &issues_model.IssuesOptions{ + ListOptions: listOptions, + RepoIDs: []int64{ctx.Repo.Repository.ID}, + IsClosed: isClosed, + LabelIDs: labelIDs, + MilestoneIDs: mileIDs, + ProjectID: projectID, + IsPull: isPull, + UpdatedBeforeUnix: before, + UpdatedAfterUnix: since, + PosterID: createdByID, + AssigneeID: assignedByID, + MentionedID: mentionedByID, + Keyword: keyword, + } + + if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } - issuesOpt.ListOptions = db.ListOptions{ - Page: -1, - } - if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) - return - } + if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return } ctx.SetTotalCountHeader(filteredCount) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 72f83f8b31c3d..06f3c01674a73 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -339,10 +339,7 @@ func ViewProject(ctx *context.Context) { } if len(referencedIds) > 0 { - if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ - IssueIDs: referencedIds, - IsPull: util.OptionalBoolTrue, - }); err == nil { + if linkedPrs, err := issues_model.FindIssuesByIDs(ctx, referencedIds, util.OptionalBoolTrue); err == nil { linkedPrsMap[issue.ID] = linkedPrs } } diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 2513fc9a98383..35b42321675fa 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -23,7 +23,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" - issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -466,36 +465,16 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { keyword := strings.Trim(ctx.FormString("q"), " ") ctx.Data["Keyword"] = keyword - // Execute keyword search for issues. - // USING NON-FINAL STATE OF opts FOR A QUERY. - issueIDsFromSearch, err := issueIDsFromSearch(ctx, ctxUser, keyword, opts) - if err != nil { - ctx.ServerError("issueIDsFromSearch", err) - return - } - - // Ensure no issues are returned if a keyword was provided that didn't match any issues. - var forceEmpty bool - - if len(issueIDsFromSearch) > 0 { - opts.IssueIDs = issueIDsFromSearch - } else if len(keyword) > 0 { - forceEmpty = true - } - // Educated guess: Do or don't show closed issues. isShowClosed := ctx.FormString("state") == "closed" opts.IsClosed = util.OptionalBoolOf(isShowClosed) // Filter repos and count issues in them. Count will be used later. // USING NON-FINAL STATE OF opts FOR A QUERY. - var issueCountByRepo map[int64]int64 - if !forceEmpty { - issueCountByRepo, err = issues_model.CountIssuesByRepo(ctx, opts) - if err != nil { - ctx.ServerError("CountIssuesByRepo", err) - return - } + issueCountByRepo, err := issues_model.CountIssuesByRepo(ctx, opts) + if err != nil { + ctx.ServerError("CountIssuesByRepo", err) + return } // Make sure page number is at least 1. Will be posted to ctx.Data. @@ -529,15 +508,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // Slice of Issues that will be displayed on the overview page // USING FINAL STATE OF opts FOR A QUERY. - var issues []*issues_model.Issue - if !forceEmpty { - issues, err = issues_model.Issues(ctx, opts) - if err != nil { - ctx.ServerError("Issues", err) - return - } - } else { - issues = []*issues_model.Issue{} + issues, err := issues_model.Issues(ctx, opts) + if err != nil { + ctx.ServerError("Issues", err) + return } // ---------------------------------- @@ -575,27 +549,22 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // ------------------------------- // Fill stats to post to ctx.Data. // ------------------------------- - var issueStats *issues_model.IssueStats - if !forceEmpty { - statsOpts := issues_model.IssuesOptions{ - User: ctx.Doer, - IsPull: util.OptionalBoolOf(isPullList), - IsClosed: util.OptionalBoolOf(isShowClosed), - IssueIDs: issueIDsFromSearch, - IsArchived: util.OptionalBoolFalse, - LabelIDs: opts.LabelIDs, - Org: org, - Team: team, - RepoCond: opts.RepoCond, - } + statsOpts := issues_model.IssuesOptions{ + User: ctx.Doer, + IsPull: util.OptionalBoolOf(isPullList), + IsClosed: util.OptionalBoolOf(isShowClosed), + IsArchived: util.OptionalBoolFalse, + LabelIDs: opts.LabelIDs, + Org: org, + Team: team, + RepoCond: opts.RepoCond, + Keyword: keyword, + } - issueStats, err = issues_model.GetUserIssueStats(filterMode, statsOpts) - if err != nil { - ctx.ServerError("GetUserIssueStats Shown", err) - return - } - } else { - issueStats = &issues_model.IssueStats{} + issueStats, err := issues_model.GetUserIssueStats(filterMode, statsOpts) + if err != nil { + ctx.ServerError("GetUserIssueStats Shown", err) + return } // Will be posted to ctx.Data. @@ -716,23 +685,6 @@ func getRepoIDs(reposQuery string) []int64 { return repoIDs } -func issueIDsFromSearch(ctx *context.Context, ctxUser *user_model.User, keyword string, opts *issues_model.IssuesOptions) ([]int64, error) { - if len(keyword) == 0 { - return []int64{}, nil - } - - searchRepoIDs, err := issues_model.GetRepoIDsForIssuesOptions(opts, ctxUser) - if err != nil { - return nil, fmt.Errorf("GetRepoIDsForIssuesOptions: %w", err) - } - issueIDsFromSearch, err := issue_indexer.SearchIssuesByKeyword(ctx, searchRepoIDs, keyword) - if err != nil { - return nil, fmt.Errorf("SearchIssuesByKeyword: %w", err) - } - - return issueIDsFromSearch, nil -} - func loadRepoByIDs(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) { totalRes := make(map[int64]*repo_model.Repository, len(issueCountByRepo)) repoIDs := make([]int64, 0, 500) From 252202d7d8857662d5d6b8ee501480ab4e681e79 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 20 May 2023 23:11:58 +0800 Subject: [PATCH 02/10] Fix test --- models/issues/issue_search.go | 13 +++++++++---- models/issues/issue_stats.go | 23 +++++++---------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 07aa096fc86c4..3c1d729916cd0 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -124,7 +124,7 @@ func applyKeywordCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Sessio db.BuildCaseInsensitiveLike("issue.name", opts.Keyword), db.BuildCaseInsensitiveLike("issue.content", opts.Keyword), builder.If(!opts.DontSearchComment, - builder.In("id", builder.Select("issue_id"). + builder.In("issue.id", builder.Select("issue_id"). From("comment"). Where(builder.And( builder.Expr("comment.issue_id = issue.id"), @@ -199,6 +199,13 @@ func applyIsPullCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session return sess } +func applyUserCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { + if opts.User != nil { + sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue())) + } + return sess +} + func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { /*if len(opts.IssueIDs) > 0 { sess.In("issue.id", opts.IssueIDs) @@ -266,9 +273,7 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { applyLabelsCondition(sess, opts) - if opts.User != nil { - sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue())) - } + applyUserCondition(sess, opts) return sess } diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index dbf7d95ba7f9d..1fe0fec6f95b8 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -137,7 +137,7 @@ func GetIssueStats(opts *IssuesOptions) (*IssueStats, error) { } // GetUserIssueStats returns issue statistic information for dashboard by given conditions. -func GetUserIssueStats(filterMode int, opts IssuesOptions) (*IssueStats, error) { +func GetUserIssueStats(filterMode int, opts *IssuesOptions) (*IssueStats, error) { if opts.User == nil { return nil, errors.New("issue stats without user") } @@ -150,29 +150,20 @@ func GetUserIssueStats(filterMode int, opts IssuesOptions) (*IssueStats, error) cond := builder.NewCond() - cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.IsTrue()}) - - if len(opts.RepoIDs) > 0 { - cond = cond.And(builder.In("issue.repo_id", opts.RepoIDs)) - } - if opts.RepoCond != nil { - cond = cond.And(opts.RepoCond) - } - - if opts.User != nil { - cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue())) - } sess := func(cond builder.Cond) *xorm.Session { s := db.GetEngine(db.DefaultContext). - Join("INNER", "repository", "`issue`.repo_id = `repository`.id"). - Where(cond) + Join("INNER", "repository", "`issue`.repo_id = `repository`.id") + + applyIsPullCondition(s, opts) + applyRepoConditions(s, opts) + applyUserCondition(s, opts) if len(opts.LabelIDs) > 0 { s.Join("INNER", "issue_label", "issue_label.issue_id = issue.id"). In("issue_label.label_id", opts.LabelIDs) } - applyKeywordCondition(s, &opts) + applyKeywordCondition(s, opts) if opts.IsArchived != util.OptionalBoolNone { s.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()}) From 2ea80b0e84918deb85598d7ca7551796bbd006a1 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 09:57:49 +0800 Subject: [PATCH 03/10] Refactor getuserstats --- models/issues/issue_stats.go | 67 ++++++++++++++---------------------- models/issues/issue_test.go | 2 +- routers/web/user/home.go | 2 +- 3 files changed, 27 insertions(+), 44 deletions(-) diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index 1fe0fec6f95b8..3935bbb91e769 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -145,19 +145,16 @@ func GetUserIssueStats(filterMode int, opts *IssuesOptions) (*IssueStats, error) return nil, errors.New("unaccepted ispull option") } - var err error stats := &IssueStats{} - cond := builder.NewCond() - - - sess := func(cond builder.Cond) *xorm.Session { + sess := func(isClosed bool) *xorm.Session { s := db.GetEngine(db.DefaultContext). - Join("INNER", "repository", "`issue`.repo_id = `repository`.id") + Join("INNER", "repository", "`issue`.repo_id = `repository`.id"). + And("issue.is_closed = ?", isClosed) - applyIsPullCondition(s, opts) - applyRepoConditions(s, opts) - applyUserCondition(s, opts) + applyIsPullCondition(s, opts) + applyRepoConditions(s, opts) + applyUserCondition(s, opts) if len(opts.LabelIDs) > 0 { s.Join("INNER", "issue_label", "issue_label.issue_id = issue.id"). In("issue_label.label_id", opts.LabelIDs) @@ -171,114 +168,100 @@ func GetUserIssueStats(filterMode int, opts *IssuesOptions) (*IssueStats, error) return s } + var err error switch filterMode { case FilterModeAll, FilterModeYourRepositories: - stats.OpenCount, err = sess(cond). - And("issue.is_closed = ?", false). - Count(new(Issue)) + stats.OpenCount, err = sess(false).Count(new(Issue)) if err != nil { return nil, err } - stats.ClosedCount, err = sess(cond). - And("issue.is_closed = ?", true). - Count(new(Issue)) + stats.ClosedCount, err = sess(true).Count(new(Issue)) if err != nil { return nil, err } case FilterModeAssign: - stats.OpenCount, err = applyAssigneeCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", false). + stats.OpenCount, err = applyAssigneeCondition(sess(false), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } - stats.ClosedCount, err = applyAssigneeCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", true). + stats.ClosedCount, err = applyAssigneeCondition(sess(true), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } case FilterModeCreate: - stats.OpenCount, err = applyPosterCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", false). + stats.OpenCount, err = applyPosterCondition(sess(false), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } - stats.ClosedCount, err = applyPosterCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", true). + stats.ClosedCount, err = applyPosterCondition(sess(true), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } case FilterModeMention: - stats.OpenCount, err = applyMentionedCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", false). + stats.OpenCount, err = applyMentionedCondition(sess(false), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } - stats.ClosedCount, err = applyMentionedCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", true). + stats.ClosedCount, err = applyMentionedCondition(sess(true), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } case FilterModeReviewRequested: - stats.OpenCount, err = applyReviewRequestedCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", false). + stats.OpenCount, err = applyReviewRequestedCondition(sess(false), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } - stats.ClosedCount, err = applyReviewRequestedCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", true). + stats.ClosedCount, err = applyReviewRequestedCondition(sess(true), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } case FilterModeReviewed: - stats.OpenCount, err = applyReviewedCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", false). + stats.OpenCount, err = applyReviewedCondition(sess(false), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } - stats.ClosedCount, err = applyReviewedCondition(sess(cond), opts.User.ID). - And("issue.is_closed = ?", true). + stats.ClosedCount, err = applyReviewedCondition(sess(true), opts.User.ID). Count(new(Issue)) if err != nil { return nil, err } } - cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed.IsTrue()}) - stats.AssignCount, err = applyAssigneeCondition(sess(cond), opts.User.ID).Count(new(Issue)) + stats.AssignCount, err = applyAssigneeCondition(sess(opts.IsClosed.IsTrue()), opts.User.ID).Count(new(Issue)) if err != nil { return nil, err } - stats.CreateCount, err = applyPosterCondition(sess(cond), opts.User.ID).Count(new(Issue)) + stats.CreateCount, err = applyPosterCondition(sess(opts.IsClosed.IsTrue()), opts.User.ID).Count(new(Issue)) if err != nil { return nil, err } - stats.MentionCount, err = applyMentionedCondition(sess(cond), opts.User.ID).Count(new(Issue)) + stats.MentionCount, err = applyMentionedCondition(sess(opts.IsClosed.IsTrue()), opts.User.ID).Count(new(Issue)) if err != nil { return nil, err } - stats.YourRepositoriesCount, err = sess(cond).Count(new(Issue)) + stats.YourRepositoriesCount, err = sess(opts.IsClosed.IsTrue()).Count(new(Issue)) if err != nil { return nil, err } - stats.ReviewRequestedCount, err = applyReviewRequestedCondition(sess(cond), opts.User.ID).Count(new(Issue)) + stats.ReviewRequestedCount, err = applyReviewRequestedCondition(sess(opts.IsClosed.IsTrue()), opts.User.ID).Count(new(Issue)) if err != nil { return nil, err } - stats.ReviewedCount, err = applyReviewedCondition(sess(cond), opts.User.ID).Count(new(Issue)) + stats.ReviewedCount, err = applyReviewedCondition(sess(opts.IsClosed.IsTrue()), opts.User.ID).Count(new(Issue)) if err != nil { return nil, err } diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 944e6003c4098..673791293f125 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -300,7 +300,7 @@ func TestGetUserIssueStats(t *testing.T) { }, } { t.Run(fmt.Sprintf("%#v", test.Opts), func(t *testing.T) { - stats, err := issues_model.GetUserIssueStats(test.FilterMode, test.Opts) + stats, err := issues_model.GetUserIssueStats(test.FilterMode, &test.Opts) if !assert.NoError(t, err) { return } diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 35b42321675fa..9ccf9b8bb36ce 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -561,7 +561,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { Keyword: keyword, } - issueStats, err := issues_model.GetUserIssueStats(filterMode, statsOpts) + issueStats, err := issues_model.GetUserIssueStats(filterMode, &statsOpts) if err != nil { ctx.ServerError("GetUserIssueStats Shown", err) return From 07725db0d066161660a51733d6b6861bf7f2a66f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 11:40:31 +0800 Subject: [PATCH 04/10] introduce issue search --- models/issues/issue_search.go | 2 +- modules/indexer/issues/indexer.go | 2 +- routers/api/v1/repo/issue.go | 119 +++++++++++------------------- routers/web/repo/issue.go | 104 +++++++++++--------------- routers/web/user/home.go | 2 +- routers/web/user/notification.go | 21 ++---- services/issue/search.go | 29 ++++++++ 7 files changed, 127 insertions(+), 152 deletions(-) create mode 100644 services/issue/search.go diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 3c1d729916cd0..e99d013fc2785 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -465,7 +465,7 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i } // Issues returns a list of issues by given conditions. -func Issues(ctx context.Context, opts *IssuesOptions) ([]*Issue, error) { +func Issues(ctx context.Context, opts *IssuesOptions) (IssueList, error) { sess := db.GetEngine(ctx). Join("INNER", "repository", "`issue`.repo_id = `repository`.id") applyLimit(sess, opts) diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 76ff80ffca7c4..1ac18033710ba 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -310,7 +310,7 @@ func UpdateRepoIndexer(ctx context.Context, repo *repo_model.Repository) { log.Error("Issues: %v", err) return } - if err = issues_model.IssueList(is).LoadDiscussComments(ctx); err != nil { + if err = is.LoadDiscussComments(ctx); err != nil { log.Error("LoadDiscussComments: %v", err) return } diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 18c5ff216e72a..bf2d3cbabf7a4 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -19,7 +19,6 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" - issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -179,27 +178,18 @@ func SearchIssues(ctx *context.APIContext) { opts.TeamID = team.ID } + // TODO: repoCond := repo_model.SearchRepositoryCondition(opts) - repoIDs, _, err := repo_model.SearchRepositoryIDs(opts) + // repoIDs, _, err := repo_model.SearchRepositoryIDs(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "SearchRepositoryIDs", err) return } - var issues []*issues_model.Issue - var filteredCount int64 - keyword := ctx.FormTrim("q") if strings.IndexByte(keyword, 0) >= 0 { keyword = "" } - var issueIDs []int64 - if len(keyword) > 0 && len(repoIDs) > 0 { - if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword); err != nil { - ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err) - return - } - } var isPull util.OptionalBool switch ctx.FormString("type") { @@ -234,58 +224,49 @@ func SearchIssues(ctx *context.APIContext) { // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. - if len(keyword) == 0 || len(issueIDs) > 0 || len(includedLabelNames) > 0 || len(includedMilestones) > 0 { - issuesOpt := &issues_model.IssuesOptions{ - ListOptions: db.ListOptions{ - Page: ctx.FormInt("page"), - PageSize: limit, - }, - RepoCond: repoCond, - IsClosed: isClosed, - IncludedLabelNames: includedLabelNames, - IncludeMilestones: includedMilestones, - SortType: "priorityrepo", - PriorityRepoID: ctx.FormInt64("priority_repo_id"), - IsPull: isPull, - UpdatedBeforeUnix: before, - UpdatedAfterUnix: since, - Keyword: keyword, - } - - ctxUserID := int64(0) - if ctx.IsSigned { - ctxUserID = ctx.Doer.ID - } - - // Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested - if ctx.FormBool("created") { - issuesOpt.PosterID = ctxUserID - } - if ctx.FormBool("assigned") { - issuesOpt.AssigneeID = ctxUserID - } - if ctx.FormBool("mentioned") { - issuesOpt.MentionedID = ctxUserID - } - if ctx.FormBool("review_requested") { - issuesOpt.ReviewRequestedID = ctxUserID - } - if ctx.FormBool("reviewed") { - issuesOpt.ReviewedID = ctxUserID - } + issuesOpt := &issues_model.IssuesOptions{ + ListOptions: db.ListOptions{ + Page: ctx.FormInt("page"), + PageSize: limit, + }, + RepoCond: repoCond, + IsClosed: isClosed, + IncludedLabelNames: includedLabelNames, + IncludeMilestones: includedMilestones, + SortType: "priorityrepo", + PriorityRepoID: ctx.FormInt64("priority_repo_id"), + IsPull: isPull, + UpdatedBeforeUnix: before, + UpdatedAfterUnix: since, + Keyword: keyword, + } + + ctxUserID := int64(0) + if ctx.IsSigned { + ctxUserID = ctx.Doer.ID + } - if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, "Issues", err) - return - } + // Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested + if ctx.FormBool("created") { + issuesOpt.PosterID = ctxUserID + } + if ctx.FormBool("assigned") { + issuesOpt.AssigneeID = ctxUserID + } + if ctx.FormBool("mentioned") { + issuesOpt.MentionedID = ctxUserID + } + if ctx.FormBool("review_requested") { + issuesOpt.ReviewRequestedID = ctxUserID + } + if ctx.FormBool("reviewed") { + issuesOpt.ReviewedID = ctxUserID + } - issuesOpt.ListOptions = db.ListOptions{ - Page: -1, - } - if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, "CountIssues", err) - return - } + filteredCount, issues, err := issue_service.Search(ctx, issuesOpt) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Issues", err) + return } ctx.SetLinkHeader(int(filteredCount), limit) @@ -384,9 +365,6 @@ func ListIssues(ctx *context.APIContext) { isClosed = util.OptionalBoolFalse } - var issues []*issues_model.Issue - var filteredCount int64 - keyword := ctx.FormTrim("q") if strings.IndexByte(keyword, 0) >= 0 { keyword = "" @@ -474,19 +452,12 @@ func ListIssues(ctx *context.APIContext) { Keyword: keyword, } - if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { + filteredCount, issues, err := issue_service.Search(ctx, issuesOpt) + if err != nil { ctx.Error(http.StatusInternalServerError, "Issues", err) return } - issuesOpt.ListOptions = db.ListOptions{ - Page: -1, - } - if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, "CountIssues", err) - return - } - ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) ctx.SetTotalCountHeader(filteredCount) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 5a90bed7c845f..af29048b33cbc 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2440,59 +2440,50 @@ func SearchIssues(ctx *context.Context) { // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. - if len(keyword) == 0 || len(includedLabelNames) > 0 || len(includedMilestones) > 0 { - issuesOpt := &issues_model.IssuesOptions{ - ListOptions: db.ListOptions{ - Page: ctx.FormInt("page"), - PageSize: limit, - }, - RepoCond: repoCond, - IsClosed: isClosed, - IncludedLabelNames: includedLabelNames, - IncludeMilestones: includedMilestones, - ProjectID: projectID, - SortType: "priorityrepo", - PriorityRepoID: ctx.FormInt64("priority_repo_id"), - IsPull: isPull, - UpdatedBeforeUnix: before, - UpdatedAfterUnix: since, - Keyword: keyword, - } - - ctxUserID := int64(0) - if ctx.IsSigned { - ctxUserID = ctx.Doer.ID - } - - // Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested - if ctx.FormBool("created") { - issuesOpt.PosterID = ctxUserID - } - if ctx.FormBool("assigned") { - issuesOpt.AssigneeID = ctxUserID - } - if ctx.FormBool("mentioned") { - issuesOpt.MentionedID = ctxUserID - } - if ctx.FormBool("review_requested") { - issuesOpt.ReviewRequestedID = ctxUserID - } - if ctx.FormBool("reviewed") { - issuesOpt.ReviewedID = ctxUserID - } + issuesOpt := &issues_model.IssuesOptions{ + ListOptions: db.ListOptions{ + Page: ctx.FormInt("page"), + PageSize: limit, + }, + RepoCond: repoCond, + IsClosed: isClosed, + IncludedLabelNames: includedLabelNames, + IncludeMilestones: includedMilestones, + ProjectID: projectID, + SortType: "priorityrepo", + PriorityRepoID: ctx.FormInt64("priority_repo_id"), + IsPull: isPull, + UpdatedBeforeUnix: before, + UpdatedAfterUnix: since, + Keyword: keyword, + } + + ctxUserID := int64(0) + if ctx.IsSigned { + ctxUserID = ctx.Doer.ID + } - if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, "Issues", err.Error()) - return - } + // Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested + if ctx.FormBool("created") { + issuesOpt.PosterID = ctxUserID + } + if ctx.FormBool("assigned") { + issuesOpt.AssigneeID = ctxUserID + } + if ctx.FormBool("mentioned") { + issuesOpt.MentionedID = ctxUserID + } + if ctx.FormBool("review_requested") { + issuesOpt.ReviewRequestedID = ctxUserID + } + if ctx.FormBool("reviewed") { + issuesOpt.ReviewedID = ctxUserID + } - issuesOpt.ListOptions = db.ListOptions{ - Page: -1, - } - if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, "CountIssues", err.Error()) - return - } + filteredCount, issues, err = issue_service.Search(ctx, issuesOpt) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Issues", err.Error()) + return } ctx.SetTotalCountHeader(filteredCount) @@ -2537,9 +2528,6 @@ func ListIssues(ctx *context.Context) { isClosed = util.OptionalBoolFalse } - var issues []*issues_model.Issue - var filteredCount int64 - keyword := ctx.FormTrim("q") if strings.IndexByte(keyword, 0) >= 0 { keyword = "" @@ -2633,12 +2621,8 @@ func ListIssues(ctx *context.Context) { Keyword: keyword, } - if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) - return - } - - if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil { + filteredCount, issues, err := issue_service.Search(ctx, issuesOpt) + if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 9ccf9b8bb36ce..34d3ec3db63ba 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -600,7 +600,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { ctx.Data["Issues"] = issues - approvalCounts, err := issues_model.IssueList(issues).GetApprovalCounts(ctx) + approvalCounts, err := issues.GetApprovalCounts(ctx) if err != nil { ctx.ServerError("ApprovalCounts", err) return diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index e0aa92879fcc4..94562b6521bff 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -252,17 +252,7 @@ func NotificationSubscriptions(ctx *context.Context) { } } - count, err := issues_model.CountIssues(ctx, &issues_model.IssuesOptions{ - SubscriberID: ctx.Doer.ID, - IsClosed: showClosed, - IsPull: issueTypeBool, - LabelIDs: labelIDs, - }) - if err != nil { - ctx.ServerError("CountIssues", err) - return - } - issues, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ + opts := &issues_model.IssuesOptions{ ListOptions: db.ListOptions{ PageSize: setting.UI.IssuePagingNum, Page: page, @@ -272,9 +262,11 @@ func NotificationSubscriptions(ctx *context.Context) { IsClosed: showClosed, IsPull: issueTypeBool, LabelIDs: labelIDs, - }) + } + + count, issues, err := issue_service.Search(ctx, opts) if err != nil { - ctx.ServerError("Issues", err) + ctx.ServerError("CountIssues", err) return } @@ -296,8 +288,7 @@ func NotificationSubscriptions(ctx *context.Context) { } ctx.Data["CommitStatus"] = commitStatus - issueList := issues_model.IssueList(issues) - approvalCounts, err := issueList.GetApprovalCounts(ctx) + approvalCounts, err := issues.GetApprovalCounts(ctx) if err != nil { ctx.ServerError("ApprovalCounts", err) return diff --git a/services/issue/search.go b/services/issue/search.go new file mode 100644 index 0000000000000..c68a9337e51d4 --- /dev/null +++ b/services/issue/search.go @@ -0,0 +1,29 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package issue + +import ( + "context" + "errors" + + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/setting" +) + +// Search search issues with sort acorrding the given conditions +func Search(ctx context.Context, opts *issues_model.IssuesOptions) (int64, issues_model.IssueList, error) { + if setting.Indexer.IssueType == "db" || opts.Keyword == "" { + issues, err := issues_model.Issues(ctx, opts) + if err != nil { + return 0, nil, err + } + total, err := issues_model.CountIssues(ctx, opts) + if err != nil { + return 0, nil, err + } + return total, issues, nil + } + + return 0, nil, errors.New("unimplementated search type") +} From 43eff7a3c7ae44d493a801aaaff9292a0601cee9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 15:06:02 +0800 Subject: [PATCH 05/10] more refactors --- models/issues/issue_search.go | 43 ------------- models/issues/issue_test.go | 25 -------- modules/indexer/issues/db.go | 56 ----------------- modules/indexer/issues/indexer.go | 18 +++--- modules/indexer/issues/indexer_test.go | 48 +++++++------- routers/web/repo/issue.go | 87 ++++++++++---------------- services/issue/search.go | 14 ++++- services/issue/search_test.go | 50 +++++++++++++++ 8 files changed, 123 insertions(+), 218 deletions(-) delete mode 100644 modules/indexer/issues/db.go create mode 100644 services/issue/search_test.go diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index e99d013fc2785..834d60ea61049 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -483,46 +483,3 @@ func Issues(ctx context.Context, opts *IssuesOptions) (IssueList, error) { return issues, nil } - -// SearchIssueIDsByKeyword search issues on database -func SearchIssueIDsByKeyword(ctx context.Context, kw string, repoIDs []int64, limit, start int) (int64, []int64, error) { - repoCond := builder.In("repo_id", repoIDs) - subQuery := builder.Select("id").From("issue").Where(repoCond) - cond := builder.And( - repoCond, - builder.Or( - db.BuildCaseInsensitiveLike("name", kw), - db.BuildCaseInsensitiveLike("content", kw), - builder.In("id", builder.Select("issue_id"). - From("comment"). - Where(builder.And( - builder.Eq{"type": CommentTypeComment}, - builder.In("issue_id", subQuery), - db.BuildCaseInsensitiveLike("content", kw), - )), - ), - ), - ) - - ids := make([]int64, 0, limit) - res := make([]struct { - ID int64 - UpdatedUnix int64 - }, 0, limit) - err := db.GetEngine(ctx).Distinct("id", "updated_unix").Table("issue").Where(cond). - OrderBy("`updated_unix` DESC").Limit(limit, start). - Find(&res) - if err != nil { - return 0, nil, err - } - for _, r := range res { - ids = append(ids, r.ID) - } - - total, err := db.GetEngine(ctx).Distinct("id").Table("issue").Where(cond).Count() - if err != nil { - return 0, nil, err - } - - return total, ids, nil -} diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 673791293f125..48fb7207e3c75 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -4,7 +4,6 @@ package issues_test import ( - "context" "fmt" "sort" "sync" @@ -317,30 +316,6 @@ func TestIssue_loadTotalTimes(t *testing.T) { assert.Equal(t, int64(3682), ms.TotalTrackedTime) } -func TestIssue_SearchIssueIDsByKeyword(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - total, ids, err := issues_model.SearchIssueIDsByKeyword(context.TODO(), "issue2", []int64{1}, 10, 0) - assert.NoError(t, err) - assert.EqualValues(t, 1, total) - assert.EqualValues(t, []int64{2}, ids) - - total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "first", []int64{1}, 10, 0) - assert.NoError(t, err) - assert.EqualValues(t, 1, total) - assert.EqualValues(t, []int64{1}, ids) - - total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "for", []int64{1}, 10, 0) - assert.NoError(t, err) - assert.EqualValues(t, 5, total) - assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) - - // issue1's comment id 2 - total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "good", []int64{1}, 10, 0) - assert.NoError(t, err) - assert.EqualValues(t, 1, total) - assert.EqualValues(t, []int64{1}, ids) -} - func TestGetRepoIDsForIssuesOptions(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) diff --git a/modules/indexer/issues/db.go b/modules/indexer/issues/db.go deleted file mode 100644 index 04c101c356900..0000000000000 --- a/modules/indexer/issues/db.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package issues - -import ( - "context" - - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" -) - -// DBIndexer implements Indexer interface to use database's like search -type DBIndexer struct{} - -// Init dummy function -func (i *DBIndexer) Init() (bool, error) { - return false, nil -} - -// Ping checks if database is available -func (i *DBIndexer) Ping() bool { - return db.GetEngine(db.DefaultContext).Ping() != nil -} - -// Index dummy function -func (i *DBIndexer) Index(issue []*IndexerData) error { - return nil -} - -// Delete dummy function -func (i *DBIndexer) Delete(ids ...int64) error { - return nil -} - -// Close dummy function -func (i *DBIndexer) Close() { -} - -// Search dummy function -func (i *DBIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) { - total, ids, err := issues_model.SearchIssueIDsByKeyword(ctx, kw, repoIDs, limit, start) - if err != nil { - return nil, err - } - result := SearchResult{ - Total: total, - Hits: make([]Match, 0, limit), - } - for _, id := range ids { - result.Hits = append(result.Hits, Match{ - ID: id, - }) - } - return &result, nil -} diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 1ac18033710ba..b2ff00188077b 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -197,10 +197,6 @@ func InitIssueIndexer(syncReindex bool) { holder.set(issueIndexer) atTerminate(finished) }) - case "db": - issueIndexer := &DBIndexer{} - holder.set(issueIndexer) - graceful.GetManager().RunAtTerminate(finished) case "meilisearch": graceful.GetManager().RunWithShutdownFns(func(_, atTerminate func(func())) { pprof.SetGoroutineLabels(ctx) @@ -361,24 +357,24 @@ func DeleteRepoIssueIndexer(ctx context.Context, repo *repo_model.Repository) { } } -// SearchIssuesByKeyword search issue ids by keywords and repo id +// Search search issue ids by keywords and repo id // WARNNING: You have to ensure user have permission to visit repoIDs' issues -func SearchIssuesByKeyword(ctx context.Context, repoIDs []int64, keyword string) ([]int64, error) { +func Search(ctx context.Context, options *issues_model.IssuesOptions) (int64, []int64, error) { var issueIDs []int64 indexer := holder.get() if indexer == nil { - log.Error("SearchIssuesByKeyword(): unable to get indexer!") - return nil, fmt.Errorf("unable to get issue indexer") + log.Error("Search(): unable to get indexer!") + return 0, nil, fmt.Errorf("unable to get issue indexer") } - res, err := indexer.Search(ctx, keyword, repoIDs, 50, 0) + res, err := indexer.Search(ctx, options.Keyword, options.RepoIDs, 50, 0) if err != nil { - return nil, err + return 0, nil, err } for _, r := range res.Hits { issueIDs = append(issueIDs, r.ID) } - return issueIDs, nil + return 0, issueIDs, nil } // IsAvailable checks if issue indexer is available diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index a2d1794f4b662..c1a68f9159097 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" @@ -50,42 +51,35 @@ func TestBleveSearchIssues(t *testing.T) { time.Sleep(5 * time.Second) - ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2") - assert.NoError(t, err) - assert.EqualValues(t, []int64{2}, ids) - - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first") - assert.NoError(t, err) - assert.EqualValues(t, []int64{1}, ids) - - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for") - assert.NoError(t, err) - assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) - - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good") - assert.NoError(t, err) - assert.EqualValues(t, []int64{1}, ids) -} - -func TestDBSearchIssues(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - setting.Indexer.IssueType = "db" - InitIssueIndexer(true) - - ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2") + total, ids, err := Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "issue2", + }) assert.NoError(t, err) assert.EqualValues(t, []int64{2}, ids) + assert.EqualValues(t, 1, total) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first") + total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "first", + }) assert.NoError(t, err) assert.EqualValues(t, []int64{1}, ids) + assert.EqualValues(t, 1, total) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for") + total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "for", + }) assert.NoError(t, err) assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) + assert.EqualValues(t, 5, total) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good") + total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "good", + }) assert.NoError(t, err) assert.EqualValues(t, []int64{1}, ids) + assert.EqualValues(t, 1, total) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index af29048b33cbc..7b033d0af3d70 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -149,7 +149,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti mentionedID int64 reviewRequestedID int64 reviewedID int64 - forceEmpty bool ) if ctx.IsSigned { @@ -186,27 +185,30 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti keyword = "" } - var issueStats *issues_model.IssueStats - if forceEmpty { - issueStats = &issues_model.IssueStats{} - } else { - issueStats, err = issues_model.GetIssueStats(&issues_model.IssuesOptions{ - RepoIDs: []int64{repo.ID}, - LabelIDs: labelIDs, - MilestoneIDs: []int64{milestoneID}, - ProjectID: projectID, - AssigneeID: assigneeID, - MentionedID: mentionedID, - PosterID: posterID, - ReviewRequestedID: reviewRequestedID, - ReviewedID: reviewedID, - IsPull: isPullOption, - Keyword: keyword, - }) - if err != nil { - ctx.ServerError("GetIssueStats", err) - return - } + var mileIDs []int64 + if milestoneID > 0 || milestoneID == db.NoConditionID { // -1 to get those issues which have no any milestone assigned + mileIDs = []int64{milestoneID} + } + + opts := &issues_model.IssuesOptions{ + RepoIDs: []int64{repo.ID}, + AssigneeID: assigneeID, + PosterID: posterID, + MentionedID: mentionedID, + ReviewRequestedID: reviewRequestedID, + ReviewedID: reviewedID, + MilestoneIDs: mileIDs, + ProjectID: projectID, + IsPull: isPullOption, + LabelIDs: labelIDs, + SortType: sortType, + Keyword: keyword, + } + + issueStats, err := issues_model.GetIssueStats(opts) + if err != nil { + ctx.ServerError("GetIssueStats", err) + return } isShowClosed := ctx.FormString("state") == "closed" @@ -228,42 +230,19 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5) - var mileIDs []int64 - if milestoneID > 0 || milestoneID == db.NoConditionID { // -1 to get those issues which have no any milestone assigned - mileIDs = []int64{milestoneID} + opts.ListOptions = db.ListOptions{ + Page: pager.Paginater.Current(), + PageSize: setting.UI.IssuePagingNum, } + opts.IsClosed = util.OptionalBoolOf(isShowClosed) - var issues []*issues_model.Issue - if forceEmpty { - issues = []*issues_model.Issue{} - } else { - issues, err = issues_model.Issues(ctx, &issues_model.IssuesOptions{ - ListOptions: db.ListOptions{ - Page: pager.Paginater.Current(), - PageSize: setting.UI.IssuePagingNum, - }, - RepoIDs: []int64{repo.ID}, - AssigneeID: assigneeID, - PosterID: posterID, - MentionedID: mentionedID, - ReviewRequestedID: reviewRequestedID, - ReviewedID: reviewedID, - MilestoneIDs: mileIDs, - ProjectID: projectID, - IsClosed: util.OptionalBoolOf(isShowClosed), - IsPull: isPullOption, - LabelIDs: labelIDs, - SortType: sortType, - Keyword: keyword, - }) - if err != nil { - ctx.ServerError("Issues", err) - return - } + issues, err := issues_model.Issues(ctx, opts) + if err != nil { + ctx.ServerError("Issues", err) + return } - issueList := issues_model.IssueList(issues) - approvalCounts, err := issueList.GetApprovalCounts(ctx) + approvalCounts, err := issues.GetApprovalCounts(ctx) if err != nil { ctx.ServerError("ApprovalCounts", err) return diff --git a/services/issue/search.go b/services/issue/search.go index c68a9337e51d4..57ddc35a8cbe7 100644 --- a/services/issue/search.go +++ b/services/issue/search.go @@ -5,10 +5,11 @@ package issue import ( "context" - "errors" issues_model "code.gitea.io/gitea/models/issues" + issues_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" ) // Search search issues with sort acorrding the given conditions @@ -25,5 +26,14 @@ func Search(ctx context.Context, opts *issues_model.IssuesOptions) (int64, issue return total, issues, nil } - return 0, nil, errors.New("unimplementated search type") + total, issueIDs, err := issues_indexer.Search(ctx, opts) + if err != nil { + return 0, nil, err + } + issues, err := issues_model.FindIssuesByIDs(ctx, issueIDs, util.OptionalBoolNone) + if err != nil { + return 0, nil, err + } + + return total, issues, nil } diff --git a/services/issue/search_test.go b/services/issue/search_test.go new file mode 100644 index 0000000000000..26c24daeafad7 --- /dev/null +++ b/services/issue/search_test.go @@ -0,0 +1,50 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package issue + +import ( + "context" + "testing" + + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestSearch(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + total, ids, err := Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "issue2", + }) + assert.NoError(t, err) + assert.EqualValues(t, []int64{2}, ids) + assert.EqualValues(t, 1, total) + + total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "first", + }) + assert.NoError(t, err) + assert.EqualValues(t, []int64{1}, ids) + assert.EqualValues(t, 1, total) + + total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "for", + }) + assert.NoError(t, err) + assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) + assert.EqualValues(t, 5, total) + + total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + RepoIDs: []int64{1}, + Keyword: "good", + }) + assert.NoError(t, err) + assert.EqualValues(t, []int64{1}, ids) + assert.EqualValues(t, 1, total) +} From 8adbbc2eba537ce1f6e743c2c75317287a244a6d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 15:24:19 +0800 Subject: [PATCH 06/10] Remove unused function --- models/issues/issue_stats.go | 29 ----------------------------- services/issue/search.go | 1 + 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index 3935bbb91e769..fa45135196a6b 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -269,35 +269,6 @@ func GetUserIssueStats(filterMode int, opts *IssuesOptions) (*IssueStats, error) return stats, nil } -// GetRepoIssueStats returns number of open and closed repository issues by given filter mode. -func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen, numClosed int64) { - countSession := func(isClosed, isPull bool, repoID int64) *xorm.Session { - sess := db.GetEngine(db.DefaultContext). - Where("is_closed = ?", isClosed). - And("is_pull = ?", isPull). - And("repo_id = ?", repoID) - - return sess - } - - openCountSession := countSession(false, isPull, repoID) - closedCountSession := countSession(true, isPull, repoID) - - switch filterMode { - case FilterModeAssign: - applyAssigneeCondition(openCountSession, uid) - applyAssigneeCondition(closedCountSession, uid) - case FilterModeCreate: - applyPosterCondition(openCountSession, uid) - applyPosterCondition(closedCountSession, uid) - } - - openResult, _ := openCountSession.Count(new(Issue)) - closedResult, _ := closedCountSession.Count(new(Issue)) - - return openResult, closedResult -} - // CountOrphanedIssues count issues without a repo func CountOrphanedIssues(ctx context.Context) (int64, error) { return db.GetEngine(ctx). diff --git a/services/issue/search.go b/services/issue/search.go index 57ddc35a8cbe7..2999c8841ab54 100644 --- a/services/issue/search.go +++ b/services/issue/search.go @@ -14,6 +14,7 @@ import ( // Search search issues with sort acorrding the given conditions func Search(ctx context.Context, opts *issues_model.IssuesOptions) (int64, issues_model.IssueList, error) { + // If the issue indexer is database or the keyword is empty, use the database search if setting.Indexer.IssueType == "db" || opts.Keyword == "" { issues, err := issues_model.Issues(ctx, opts) if err != nil { From 67e71aa4adef3fd3ab77e8f7bfd509cd48bd6bbc Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 15:45:48 +0800 Subject: [PATCH 07/10] Adjust interface --- modules/indexer/issues/bleve.go | 13 +++++++------ modules/indexer/issues/bleve_test.go | 11 ++++++++++- modules/indexer/issues/elastic_search.go | 15 ++++++++------- modules/indexer/issues/indexer.go | 9 ++++++--- modules/indexer/issues/indexer_test.go | 8 ++++---- modules/indexer/issues/meilisearch.go | 13 +++++++------ services/issue/search.go | 2 +- 7 files changed, 43 insertions(+), 28 deletions(-) diff --git a/modules/indexer/issues/bleve.go b/modules/indexer/issues/bleve.go index 60d9ef76174f6..4b0799758de98 100644 --- a/modules/indexer/issues/bleve.go +++ b/modules/indexer/issues/bleve.go @@ -9,6 +9,7 @@ import ( "os" "strconv" + issues_model "code.gitea.io/gitea/models/issues" gitea_bleve "code.gitea.io/gitea/modules/indexer/bleve" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" @@ -235,9 +236,9 @@ func (b *BleveIndexer) Delete(ids ...int64) error { // Search searches for issues by given conditions. // Returns the matching issue IDs -func (b *BleveIndexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*SearchResult, error) { +func (b *BleveIndexer) Search(ctx context.Context, opts *issues_model.IssuesOptions) (*SearchResult, error) { var repoQueriesP []*query.NumericRangeQuery - for _, repoID := range repoIDs { + for _, repoID := range opts.RepoIDs { repoQueriesP = append(repoQueriesP, numericEqualityQuery(repoID, "RepoID")) } repoQueries := make([]query.Query, len(repoQueriesP)) @@ -248,11 +249,11 @@ func (b *BleveIndexer) Search(ctx context.Context, keyword string, repoIDs []int indexerQuery := bleve.NewConjunctionQuery( bleve.NewDisjunctionQuery(repoQueries...), bleve.NewDisjunctionQuery( - newMatchPhraseQuery(keyword, "Title", issueIndexerAnalyzer), - newMatchPhraseQuery(keyword, "Content", issueIndexerAnalyzer), - newMatchPhraseQuery(keyword, "Comments", issueIndexerAnalyzer), + newMatchPhraseQuery(opts.Keyword, "Title", issueIndexerAnalyzer), + newMatchPhraseQuery(opts.Keyword, "Content", issueIndexerAnalyzer), + newMatchPhraseQuery(opts.Keyword, "Comments", issueIndexerAnalyzer), )) - search := bleve.NewSearchRequestOptions(indexerQuery, limit, start, false) + search := bleve.NewSearchRequestOptions(indexerQuery, opts.PageSize, (opts.Page-1)*opts.PageSize, false) search.SortBy([]string{"-_score"}) result, err := b.indexer.SearchInContext(ctx, search) diff --git a/modules/indexer/issues/bleve_test.go b/modules/indexer/issues/bleve_test.go index 22827158e4c07..dafa8eea76e0e 100644 --- a/modules/indexer/issues/bleve_test.go +++ b/modules/indexer/issues/bleve_test.go @@ -7,6 +7,9 @@ import ( "context" "testing" + "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" + "github.com/stretchr/testify/assert" ) @@ -75,7 +78,13 @@ func TestBleveIndexAndSearch(t *testing.T) { } for _, kw := range keywords { - res, err := indexer.Search(context.TODO(), kw.Keyword, []int64{2}, 10, 0) + res, err := indexer.Search(context.TODO(), &issues_model.IssuesOptions{ + ListOptions: db.ListOptions{ + PageSize: 10, + }, + Keyword: kw.Keyword, + RepoIDs: []int64{2}, + }) assert.NoError(t, err) ids := make([]int64, 0, len(res.Hits)) diff --git a/modules/indexer/issues/elastic_search.go b/modules/indexer/issues/elastic_search.go index fd1dd4b452325..8278244d404ea 100644 --- a/modules/indexer/issues/elastic_search.go +++ b/modules/indexer/issues/elastic_search.go @@ -12,6 +12,7 @@ import ( "sync" "time" + issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -217,13 +218,13 @@ func (b *ElasticSearchIndexer) Delete(ids ...int64) error { // Search searches for issues by given conditions. // Returns the matching issue IDs -func (b *ElasticSearchIndexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*SearchResult, error) { - kwQuery := elastic.NewMultiMatchQuery(keyword, "title", "content", "comments") +func (b *ElasticSearchIndexer) Search(ctx context.Context, opts *issues_model.IssuesOptions) (*SearchResult, error) { + kwQuery := elastic.NewMultiMatchQuery(opts.Keyword, "title", "content", "comments") query := elastic.NewBoolQuery() query = query.Must(kwQuery) - if len(repoIDs) > 0 { - repoStrs := make([]interface{}, 0, len(repoIDs)) - for _, repoID := range repoIDs { + if len(opts.RepoIDs) > 0 { + repoStrs := make([]interface{}, 0, len(opts.RepoIDs)) + for _, repoID := range opts.RepoIDs { repoStrs = append(repoStrs, repoID) } repoQuery := elastic.NewTermsQuery("repo_id", repoStrs...) @@ -233,13 +234,13 @@ func (b *ElasticSearchIndexer) Search(ctx context.Context, keyword string, repoI Index(b.indexerName). Query(query). Sort("_score", false). - From(start).Size(limit). + From((opts.Page - 1) * opts.PageSize).Size(opts.PageSize). Do(ctx) if err != nil { return nil, b.checkError(err) } - hits := make([]Match, 0, limit) + hits := make([]Match, 0, opts.PageSize) for _, hit := range searchResult.Hits.Hits { id, _ := strconv.ParseInt(hit.Id, 10, 64) hits = append(hits, Match{ diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index b2ff00188077b..6b7822224d8b6 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -51,7 +51,7 @@ type Indexer interface { Ping() bool Index(issue []*IndexerData) error Delete(ids ...int64) error - Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) + Search(ctx context.Context, opts *issues_model.IssuesOptions) (*SearchResult, error) Close() } @@ -359,7 +359,7 @@ func DeleteRepoIssueIndexer(ctx context.Context, repo *repo_model.Repository) { // Search search issue ids by keywords and repo id // WARNNING: You have to ensure user have permission to visit repoIDs' issues -func Search(ctx context.Context, options *issues_model.IssuesOptions) (int64, []int64, error) { +func Search(ctx context.Context, options issues_model.IssuesOptions) (int64, []int64, error) { var issueIDs []int64 indexer := holder.get() @@ -367,7 +367,10 @@ func Search(ctx context.Context, options *issues_model.IssuesOptions) (int64, [] log.Error("Search(): unable to get indexer!") return 0, nil, fmt.Errorf("unable to get issue indexer") } - res, err := indexer.Search(ctx, options.Keyword, options.RepoIDs, 50, 0) + // reset the result + options.PageSize = 50 + options.Page = 0 + res, err := indexer.Search(ctx, &options) if err != nil { return 0, nil, err } diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index c1a68f9159097..e75f10271fa0f 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -51,7 +51,7 @@ func TestBleveSearchIssues(t *testing.T) { time.Sleep(5 * time.Second) - total, ids, err := Search(context.TODO(), &issues_model.IssuesOptions{ + total, ids, err := Search(context.TODO(), issues_model.IssuesOptions{ RepoIDs: []int64{1}, Keyword: "issue2", }) @@ -59,7 +59,7 @@ func TestBleveSearchIssues(t *testing.T) { assert.EqualValues(t, []int64{2}, ids) assert.EqualValues(t, 1, total) - total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + total, ids, err = Search(context.TODO(), issues_model.IssuesOptions{ RepoIDs: []int64{1}, Keyword: "first", }) @@ -67,7 +67,7 @@ func TestBleveSearchIssues(t *testing.T) { assert.EqualValues(t, []int64{1}, ids) assert.EqualValues(t, 1, total) - total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + total, ids, err = Search(context.TODO(), issues_model.IssuesOptions{ RepoIDs: []int64{1}, Keyword: "for", }) @@ -75,7 +75,7 @@ func TestBleveSearchIssues(t *testing.T) { assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) assert.EqualValues(t, 5, total) - total, ids, err = Search(context.TODO(), &issues_model.IssuesOptions{ + total, ids, err = Search(context.TODO(), issues_model.IssuesOptions{ RepoIDs: []int64{1}, Keyword: "good", }) diff --git a/modules/indexer/issues/meilisearch.go b/modules/indexer/issues/meilisearch.go index 990bc57a05f5d..5d0bb42e0841d 100644 --- a/modules/indexer/issues/meilisearch.go +++ b/modules/indexer/issues/meilisearch.go @@ -10,6 +10,7 @@ import ( "sync" "time" + issues_model "code.gitea.io/gitea/models/issues" "github.com/meilisearch/meilisearch-go" ) @@ -112,16 +113,16 @@ func (b *MeilisearchIndexer) Delete(ids ...int64) error { // Search searches for issues by given conditions. // Returns the matching issue IDs -func (b *MeilisearchIndexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*SearchResult, error) { - repoFilters := make([]string, 0, len(repoIDs)) - for _, repoID := range repoIDs { +func (b *MeilisearchIndexer) Search(ctx context.Context, opts *issues_model.IssuesOptions) (*SearchResult, error) { + repoFilters := make([]string, 0, len(opts.RepoIDs)) + for _, repoID := range opts.RepoIDs { repoFilters = append(repoFilters, "repo_id = "+strconv.FormatInt(repoID, 10)) } filter := strings.Join(repoFilters, " OR ") - searchRes, err := b.client.Index(b.indexerName).Search(keyword, &meilisearch.SearchRequest{ + searchRes, err := b.client.Index(b.indexerName).Search(opts.Keyword, &meilisearch.SearchRequest{ Filter: filter, - Limit: int64(limit), - Offset: int64(start), + Limit: int64(opts.PageSize), + Offset: int64((opts.Page - 1) * opts.PageSize), }) if err != nil { return nil, b.checkError(err) diff --git a/services/issue/search.go b/services/issue/search.go index 2999c8841ab54..2fe698c1a0713 100644 --- a/services/issue/search.go +++ b/services/issue/search.go @@ -27,7 +27,7 @@ func Search(ctx context.Context, opts *issues_model.IssuesOptions) (int64, issue return total, issues, nil } - total, issueIDs, err := issues_indexer.Search(ctx, opts) + total, issueIDs, err := issues_indexer.Search(ctx, *opts) if err != nil { return 0, nil, err } From 1f2a66cb8987eb89e441582573ae4475efb448d9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 15:52:12 +0800 Subject: [PATCH 08/10] Fix bleve search bug --- modules/indexer/issues/bleve.go | 3 ++- modules/indexer/issues/indexer.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/indexer/issues/bleve.go b/modules/indexer/issues/bleve.go index 4b0799758de98..20a12d4edc636 100644 --- a/modules/indexer/issues/bleve.go +++ b/modules/indexer/issues/bleve.go @@ -262,7 +262,8 @@ func (b *BleveIndexer) Search(ctx context.Context, opts *issues_model.IssuesOpti } ret := SearchResult{ - Hits: make([]Match, 0, len(result.Hits)), + Total: int64(result.Total), + Hits: make([]Match, 0, len(result.Hits)), } for _, hit := range result.Hits { id, err := idOfIndexerID(hit.ID) diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 6b7822224d8b6..ed5993822cefc 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -369,7 +369,7 @@ func Search(ctx context.Context, options issues_model.IssuesOptions) (int64, []i } // reset the result options.PageSize = 50 - options.Page = 0 + options.Page = 1 res, err := indexer.Search(ctx, &options) if err != nil { return 0, nil, err @@ -377,7 +377,7 @@ func Search(ctx context.Context, options issues_model.IssuesOptions) (int64, []i for _, r := range res.Hits { issueIDs = append(issueIDs, r.ID) } - return 0, issueIDs, nil + return res.Total, issueIDs, nil } // IsAvailable checks if issue indexer is available From f3d974455ee5f0c700904236cbb96cc7d7af0ed8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 16:19:47 +0800 Subject: [PATCH 09/10] Fix lint --- modules/indexer/issues/meilisearch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/indexer/issues/meilisearch.go b/modules/indexer/issues/meilisearch.go index 5d0bb42e0841d..3feac1a51c842 100644 --- a/modules/indexer/issues/meilisearch.go +++ b/modules/indexer/issues/meilisearch.go @@ -11,6 +11,7 @@ import ( "time" issues_model "code.gitea.io/gitea/models/issues" + "github.com/meilisearch/meilisearch-go" ) From 6a5627c3eb2fb9371ab46a52c93c3f6f89763b1c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 21 May 2023 22:17:44 +0800 Subject: [PATCH 10/10] adjust search conditions --- models/issues/issue_search.go | 125 ++++++++++++++++-------------- models/issues/issue_stats.go | 32 +++----- modules/indexer/issues/indexer.go | 2 + 3 files changed, 80 insertions(+), 79 deletions(-) diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 834d60ea61049..d88cadc92416a 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -206,43 +206,42 @@ func applyUserCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { return sess } -func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { - /*if len(opts.IssueIDs) > 0 { - sess.In("issue.id", opts.IssueIDs) - }*/ - - applyKeywordCondition(sess, opts) - - applyRepoConditions(sess, opts) - - if !opts.IsClosed.IsNone() { - sess.And("issue.is_closed=?", opts.IsClosed.IsTrue()) +func applyProjectConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { + if opts.ProjectID > 0 { + sess.Join("INNER", "project_issue", "issue.id = project_issue.issue_id"). + And("project_issue.project_id=?", opts.ProjectID) + } else if opts.ProjectID == db.NoConditionID { // show those that are in no project + sess.And(builder.NotIn("issue.id", builder.Select("issue_id").From("project_issue"))) } - applyAssigneeCondition(sess, opts.AssigneeID) - - if opts.PosterID > 0 { - applyPosterCondition(sess, opts.PosterID) + if opts.ProjectBoardID != 0 { + if opts.ProjectBoardID > 0 { + sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": opts.ProjectBoardID})) + } else { + sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": 0})) + } } - if opts.MentionedID > 0 { - applyMentionedCondition(sess, opts.MentionedID) - } + return sess +} - if opts.ReviewRequestedID > 0 { - applyReviewRequestedCondition(sess, opts.ReviewRequestedID) - } +func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { + applyRepoConditions(sess, opts) - if opts.ReviewedID > 0 { - applyReviewedCondition(sess, opts.ReviewedID) + if opts.IsArchived != util.OptionalBoolNone { + sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()}) } - if opts.SubscriberID > 0 { - applySubscribedCondition(sess, opts.SubscriberID) + if !opts.IsClosed.IsNone() { + sess.And("issue.is_closed=?", opts.IsClosed.IsTrue()) } + applyIsPullCondition(sess, opts) + applyMilestoneCondition(sess, opts) + applyPosterCondition(sess, opts.PosterID) + if opts.UpdatedAfterUnix != 0 { sess.And(builder.Gte{"issue.updated_unix": opts.UpdatedAfterUnix}) } @@ -250,31 +249,24 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { sess.And(builder.Lte{"issue.updated_unix": opts.UpdatedBeforeUnix}) } - if opts.ProjectID > 0 { - sess.Join("INNER", "project_issue", "issue.id = project_issue.issue_id"). - And("project_issue.project_id=?", opts.ProjectID) - } else if opts.ProjectID == db.NoConditionID { // show those that are in no project - sess.And(builder.NotIn("issue.id", builder.Select("issue_id").From("project_issue"))) - } + applyAssigneeCondition(sess, opts.AssigneeID) - if opts.ProjectBoardID != 0 { - if opts.ProjectBoardID > 0 { - sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": opts.ProjectBoardID})) - } else { - sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": 0})) - } - } + applyMentionedCondition(sess, opts.MentionedID) - applyIsPullCondition(sess, opts) + applyReviewRequestedCondition(sess, opts.ReviewRequestedID) - if opts.IsArchived != util.OptionalBoolNone { - sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()}) - } + applyReviewedCondition(sess, opts.ReviewedID) + + applySubscribedCondition(sess, opts) + + applyProjectConditions(sess, opts) applyLabelsCondition(sess, opts) applyUserCondition(sess, opts) + applyKeywordCondition(sess, opts) + return sess } @@ -365,25 +357,38 @@ func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session } func applyPosterCondition(sess *xorm.Session, posterID int64) *xorm.Session { - return sess.And("issue.poster_id=?", posterID) + if posterID > 0 { + return sess.And("issue.poster_id=?", posterID) + } + return sess } func applyMentionedCondition(sess *xorm.Session, mentionedID int64) *xorm.Session { - return sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id"). - And("issue_user.is_mentioned = ?", true). - And("issue_user.uid = ?", mentionedID) + if mentionedID > 0 { + return sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id"). + And("issue_user.is_mentioned = ?", true). + And("issue_user.uid = ?", mentionedID) + } + return sess } func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64) *xorm.Session { - return sess.Join("INNER", []string{"review", "r"}, "issue.id = r.issue_id"). - And("issue.poster_id <> ?", reviewRequestedID). - And("r.type = ?", ReviewTypeRequest). - And("r.reviewer_id = ? and r.id in (select max(id) from review where issue_id = r.issue_id and reviewer_id = r.reviewer_id and type in (?, ?, ?))"+ - " or r.reviewer_team_id in (select team_id from team_user where uid = ?)", - reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID) + if reviewRequestedID > 0 { + return sess.Join("INNER", []string{"review", "r"}, "issue.id = r.issue_id"). + And("issue.poster_id <> ?", reviewRequestedID). + And("r.type = ?", ReviewTypeRequest). + And("r.reviewer_id = ? and r.id in (select max(id) from review where issue_id = r.issue_id and reviewer_id = r.reviewer_id and type in (?, ?, ?))"+ + " or r.reviewer_team_id in (select team_id from team_user where uid = ?)", + reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID) + } + return sess } func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session { + if reviewedID == 0 { + return sess + } + // Query for pull requests where you are a reviewer or commenter, excluding // any pull requests already returned by the the review requested filter. notPoster := builder.Neq{"issue.poster_id": reviewedID} @@ -413,31 +418,35 @@ func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session return sess.And(notPoster, builder.Or(reviewed, commented)) } -func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session { +func applySubscribedCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session { + if opts.SubscriberID == 0 { + return sess + } + return sess.And( builder. NotIn("issue.id", builder.Select("issue_id"). From("issue_watch"). - Where(builder.Eq{"is_watching": false, "user_id": subscriberID}), + Where(builder.Eq{"is_watching": false, "user_id": opts.SubscriberID}), ), ).And( builder.Or( builder.In("issue.id", builder. Select("issue_id"). From("issue_watch"). - Where(builder.Eq{"is_watching": true, "user_id": subscriberID}), + Where(builder.Eq{"is_watching": true, "user_id": opts.SubscriberID}), ), builder.In("issue.id", builder. Select("issue_id"). From("comment"). - Where(builder.Eq{"poster_id": subscriberID}), + Where(builder.Eq{"poster_id": opts.SubscriberID}), ), - builder.Eq{"issue.poster_id": subscriberID}, + builder.Eq{"issue.poster_id": opts.SubscriberID}, builder.In("issue.repo_id", builder. Select("id"). From("watch"). - Where(builder.And(builder.Eq{"user_id": subscriberID}, + Where(builder.And(builder.Eq{"user_id": opts.SubscriberID}, builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))), ), ), diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index fa45135196a6b..51ec245ef8899 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -87,38 +87,28 @@ func GetIssueStats(opts *IssuesOptions) (*IssueStats, error) { countSession := func(opts *IssuesOptions) *xorm.Session { sess := db.GetEngine(db.DefaultContext). Join("INNER", "repository", "`issue`.repo_id = `repository`.id") - applyRepoConditions(sess, opts) - applyKeywordCondition(sess, opts) + applyRepoConditions(sess, opts) - applyLabelsCondition(sess, opts) + applyIsPullCondition(sess, opts) applyMilestoneCondition(sess, opts) - if opts.ProjectID > 0 { - sess.Join("INNER", "project_issue", "issue.id = project_issue.issue_id"). - And("project_issue.project_id=?", opts.ProjectID) - } + applyPosterCondition(sess, opts.PosterID) - applyAssigneeCondition(sess, opts.AssigneeID) + applyKeywordCondition(sess, opts) - if opts.PosterID > 0 { - applyPosterCondition(sess, opts.PosterID) - } + applyLabelsCondition(sess, opts) - if opts.MentionedID > 0 { - applyMentionedCondition(sess, opts.MentionedID) - } + applyProjectConditions(sess, opts) - if opts.ReviewRequestedID > 0 { - applyReviewRequestedCondition(sess, opts.ReviewRequestedID) - } + applyAssigneeCondition(sess, opts.AssigneeID) - if opts.ReviewedID > 0 { - applyReviewedCondition(sess, opts.ReviewedID) - } + applyMentionedCondition(sess, opts.MentionedID) - applyIsPullCondition(sess, opts) + applyReviewRequestedCondition(sess, opts.ReviewRequestedID) + + applyReviewedCondition(sess, opts.ReviewedID) return sess } diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index ed5993822cefc..f5c41851ee83b 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -154,6 +154,8 @@ func InitIssueIndexer(syncReindex bool) { log.Info("PID %d: Initializing Issue Indexer: %s", os.Getpid(), setting.Indexer.IssueType) var populate bool switch setting.Indexer.IssueType { + case "db": + // Do nothing because search code will detect and place this here to prevent error prompt case "bleve": defer func() { if err := recover(); err != nil {