* 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. | |||
| type IssuesOptions struct { | |||
| 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 | |||
| 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 | |||
| @@ -269,6 +269,17 @@ func GetLabelIDsInRepoByNames(repoID int64, labelNames []string) ([]int64, error | |||
| 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 | |||
| // repositories. | |||
| // 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.AllLimited = true | |||
| } | |||
| issueCount := 0 | |||
| for page := 1; ; page++ { | |||
| opts.Page = page | |||
| @@ -127,15 +128,6 @@ func SearchIssues(ctx *context.APIContext) { | |||
| 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 | |||
| switch ctx.Query("type") { | |||
| case "pulls": | |||
| @@ -146,6 +138,12 @@ func SearchIssues(ctx *context.APIContext) { | |||
| 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 | |||
| // This would otherwise return all issues if no issues were found by the search. | |||
| if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { | |||
| @@ -154,13 +152,13 @@ func SearchIssues(ctx *context.APIContext) { | |||
| Page: ctx.QueryInt("page"), | |||
| 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, | |||
| }) | |||
| } | |||