* Use subquery to instead In * Support excludedLabelNames on issues options * Fix tests Co-authored-by: zeripath <art27@cantab.net>tags/v1.21.12.1
| @@ -1045,16 +1045,18 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) { | |||||
| // IssuesOptions represents options of an issue. | // IssuesOptions represents options of an issue. | ||||
| type IssuesOptions struct { | type IssuesOptions struct { | ||||
| ListOptions | ListOptions | ||||
| RepoIDs []int64 // include all repos if empty | |||||
| AssigneeID int64 | |||||
| PosterID int64 | |||||
| MentionedID int64 | |||||
| MilestoneID int64 | |||||
| IsClosed util.OptionalBool | |||||
| IsPull util.OptionalBool | |||||
| LabelIDs []int64 | |||||
| SortType string | |||||
| IssueIDs []int64 | |||||
| RepoIDs []int64 // include all repos if empty | |||||
| AssigneeID int64 | |||||
| PosterID int64 | |||||
| MentionedID int64 | |||||
| MilestoneID int64 | |||||
| IsClosed util.OptionalBool | |||||
| IsPull util.OptionalBool | |||||
| LabelIDs []int64 | |||||
| IncludedLabelNames []string | |||||
| ExcludedLabelNames []string | |||||
| SortType string | |||||
| IssueIDs []int64 | |||||
| // prioritize issues from this repo | // prioritize issues from this repo | ||||
| PriorityRepoID int64 | PriorityRepoID int64 | ||||
| } | } | ||||
| @@ -1153,6 +1155,14 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) { | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if len(opts.IncludedLabelNames) > 0 { | |||||
| sess.In("issue.id", BuildLabelNamesIssueIDsCondition(opts.IncludedLabelNames)) | |||||
| } | |||||
| if len(opts.ExcludedLabelNames) > 0 { | |||||
| sess.And(builder.NotIn("issue.id", BuildLabelNamesIssueIDsCondition(opts.ExcludedLabelNames))) | |||||
| } | |||||
| } | } | ||||
| // CountIssuesByRepo map from repoID to number of issues matching the options | // CountIssuesByRepo map from repoID to number of issues matching the options | ||||
| @@ -269,6 +269,17 @@ func GetLabelIDsInRepoByNames(repoID int64, labelNames []string) ([]int64, error | |||||
| Find(&labelIDs) | Find(&labelIDs) | ||||
| } | } | ||||
| // BuildLabelNamesIssueIDsCondition returns a builder where get issue ids match label names | |||||
| func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder { | |||||
| return builder.Select("issue_label.issue_id"). | |||||
| From("issue_label"). | |||||
| InnerJoin("label", "label.id = issue_label.label_id"). | |||||
| Where( | |||||
| builder.In("label.name", labelNames), | |||||
| ). | |||||
| GroupBy("issue_label.issue_id") | |||||
| } | |||||
| // GetLabelIDsInReposByNames returns a list of labelIDs by names in one of the given | // GetLabelIDsInReposByNames returns a list of labelIDs by names in one of the given | ||||
| // repositories. | // repositories. | ||||
| // it silently ignores label names that do not belong to the repository. | // it silently ignores label names that do not belong to the repository. | ||||
| @@ -88,6 +88,7 @@ func SearchIssues(ctx *context.APIContext) { | |||||
| opts.Private = true | opts.Private = true | ||||
| opts.AllLimited = true | opts.AllLimited = true | ||||
| } | } | ||||
| issueCount := 0 | issueCount := 0 | ||||
| for page := 1; ; page++ { | for page := 1; ; page++ { | ||||
| opts.Page = page | opts.Page = page | ||||
| @@ -127,15 +128,6 @@ func SearchIssues(ctx *context.APIContext) { | |||||
| issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword) | issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword) | ||||
| } | } | ||||
| labels := ctx.Query("labels") | |||||
| if splitted := strings.Split(labels, ","); labels != "" && len(splitted) > 0 { | |||||
| labelIDs, err = models.GetLabelIDsInReposByNames(repoIDs, splitted) | |||||
| if err != nil { | |||||
| ctx.Error(http.StatusInternalServerError, "GetLabelIDsInRepoByNames", err) | |||||
| return | |||||
| } | |||||
| } | |||||
| var isPull util.OptionalBool | var isPull util.OptionalBool | ||||
| switch ctx.Query("type") { | switch ctx.Query("type") { | ||||
| case "pulls": | case "pulls": | ||||
| @@ -146,6 +138,12 @@ func SearchIssues(ctx *context.APIContext) { | |||||
| isPull = util.OptionalBoolNone | isPull = util.OptionalBoolNone | ||||
| } | } | ||||
| labels := strings.TrimSpace(ctx.Query("labels")) | |||||
| var includedLabelNames []string | |||||
| if len(labels) > 0 { | |||||
| includedLabelNames = strings.Split(labels, ",") | |||||
| } | |||||
| // Only fetch the issues if we either don't have a keyword or the search returned issues | // 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. | // This would otherwise return all issues if no issues were found by the search. | ||||
| if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { | if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { | ||||
| @@ -154,13 +152,13 @@ func SearchIssues(ctx *context.APIContext) { | |||||
| Page: ctx.QueryInt("page"), | Page: ctx.QueryInt("page"), | ||||
| PageSize: setting.UI.IssuePagingNum, | PageSize: setting.UI.IssuePagingNum, | ||||
| }, | }, | ||||
| RepoIDs: repoIDs, | |||||
| IsClosed: isClosed, | |||||
| IssueIDs: issueIDs, | |||||
| LabelIDs: labelIDs, | |||||
| SortType: "priorityrepo", | |||||
| PriorityRepoID: ctx.QueryInt64("priority_repo_id"), | |||||
| IsPull: isPull, | |||||
| RepoIDs: repoIDs, | |||||
| IsClosed: isClosed, | |||||
| IssueIDs: issueIDs, | |||||
| IncludedLabelNames: includedLabelNames, | |||||
| SortType: "priorityrepo", | |||||
| PriorityRepoID: ctx.QueryInt64("priority_repo_id"), | |||||
| IsPull: isPull, | |||||
| }) | }) | ||||
| } | } | ||||