diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index b238f831ae5..e9f116bfc6e 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -118,29 +118,29 @@ func applyLabelsCondition(sess *xorm.Session, opts *IssuesOptions) { if opts.LabelIDs[0] == 0 { sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)") } else { - // We sort and deduplicate the labels' ids - IncludedLabelIDs := make(container.Set[int64]) - ExcludedLabelIDs := make(container.Set[int64]) + // deduplicate the label IDs for inclusion and exclusion + includedLabelIDs := make(container.Set[int64]) + excludedLabelIDs := make(container.Set[int64]) for _, labelID := range opts.LabelIDs { if labelID > 0 { - IncludedLabelIDs.Add(labelID) + includedLabelIDs.Add(labelID) } else if labelID < 0 { // 0 is not supported here, so just ignore it - ExcludedLabelIDs.Add(-labelID) + excludedLabelIDs.Add(-labelID) } } // ... and use them in a subquery of the form : // where (select count(*) from issue_label where issue_id=issue.id and label_id in (2, 4, 6)) = 3 // This equality is guaranteed thanks to unique index (issue_id,label_id) on table issue_label. - if len(IncludedLabelIDs) > 0 { - subquery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")). - And(builder.In("label_id", IncludedLabelIDs.Values())) - sess.Where(builder.Eq{strconv.Itoa(len(IncludedLabelIDs)): subquery}) + if len(includedLabelIDs) > 0 { + subQuery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")). + And(builder.In("label_id", includedLabelIDs.Values())) + sess.Where(builder.Eq{strconv.Itoa(len(includedLabelIDs)): subQuery}) } // or (select count(*)...) = 0 for excluded labels - if len(ExcludedLabelIDs) > 0 { - subquery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")). - And(builder.In("label_id", ExcludedLabelIDs.Values())) - sess.Where(builder.Eq{"0": subquery}) + if len(excludedLabelIDs) > 0 { + subQuery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")). + And(builder.In("label_id", excludedLabelIDs.Values())) + sess.Where(builder.Eq{"0": subQuery}) } } } diff --git a/models/issues/label.go b/models/issues/label.go index 2d7acb7f0c5..2530f71004d 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -12,6 +12,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/timeutil" @@ -143,37 +144,32 @@ func (l *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) { // LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked func (l *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) { - labelQuerySlice := []int64{} + labelQueryParams := container.Set[string]{} labelSelected := false - labelScope := l.ExclusiveScope() - for i, s := range currentSelectedLabels { - if s == l.ID { + exclusiveScope := l.ExclusiveScope() + for i, curSel := range currentSelectedLabels { + if curSel == l.ID { labelSelected = true - } else if -s == l.ID { + } else if -curSel == l.ID { labelSelected = true l.IsExcluded = true - } else if s != 0 { + } else if curSel != 0 { // Exclude other labels in the same scope from selection - if s < 0 || labelScope == "" || labelScope != currentSelectedExclusiveScopes[i] { - labelQuerySlice = append(labelQuerySlice, s) + if curSel < 0 || exclusiveScope == "" || exclusiveScope != currentSelectedExclusiveScopes[i] { + labelQueryParams.Add(strconv.FormatInt(curSel, 10)) } } } if !labelSelected { - labelQuerySlice = append(labelQuerySlice, l.ID) + labelQueryParams.Add(strconv.FormatInt(l.ID, 10)) } l.IsSelected = labelSelected // Sort and deduplicate the ids to avoid the crawlers asking for the // same thing with simply a different order of parameters - slices.Sort(labelQuerySlice) - labelQuerySlice = slices.Compact(labelQuerySlice) - // Quick conversion (strings.Join() doesn't accept slices of Int64) - labelQuerySliceStrings := make([]string, len(labelQuerySlice)) - for i, x := range labelQuerySlice { - labelQuerySliceStrings[i] = strconv.FormatInt(x, 10) - } + labelQuerySliceStrings := labelQueryParams.Values() + slices.Sort(labelQuerySliceStrings) // the sort is still needed because the underlying map of Set doesn't guarantee order l.QueryString = strings.Join(labelQuerySliceStrings, ",") } @@ -187,7 +183,7 @@ func (l *Label) BelongsToRepo() bool { return l.RepoID > 0 } -// Return scope substring of label name, or empty string if none exists +// ExclusiveScope returns scope substring of label name, or empty string if none exists func (l *Label) ExclusiveScope() string { if !l.Exclusive { return ""