Refactor issue label selection (#31497)

Follow #26460
This commit is contained in:
wxiaoguang 2024-06-27 07:41:59 +08:00 committed by GitHub
parent a88f718c10
commit 00fc29aee1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 30 deletions

View File

@ -118,29 +118,29 @@ func applyLabelsCondition(sess *xorm.Session, opts *IssuesOptions) {
if opts.LabelIDs[0] == 0 { if opts.LabelIDs[0] == 0 {
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)") sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)")
} else { } else {
// We sort and deduplicate the labels' ids // deduplicate the label IDs for inclusion and exclusion
IncludedLabelIDs := make(container.Set[int64]) includedLabelIDs := make(container.Set[int64])
ExcludedLabelIDs := make(container.Set[int64]) excludedLabelIDs := make(container.Set[int64])
for _, labelID := range opts.LabelIDs { for _, labelID := range opts.LabelIDs {
if labelID > 0 { if labelID > 0 {
IncludedLabelIDs.Add(labelID) includedLabelIDs.Add(labelID)
} else if labelID < 0 { // 0 is not supported here, so just ignore it } 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 : // ... 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 // 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. // This equality is guaranteed thanks to unique index (issue_id,label_id) on table issue_label.
if len(IncludedLabelIDs) > 0 { if len(includedLabelIDs) > 0 {
subquery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")). subQuery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")).
And(builder.In("label_id", IncludedLabelIDs.Values())) And(builder.In("label_id", includedLabelIDs.Values()))
sess.Where(builder.Eq{strconv.Itoa(len(IncludedLabelIDs)): subquery}) sess.Where(builder.Eq{strconv.Itoa(len(includedLabelIDs)): subQuery})
} }
// or (select count(*)...) = 0 for excluded labels // or (select count(*)...) = 0 for excluded labels
if len(ExcludedLabelIDs) > 0 { if len(excludedLabelIDs) > 0 {
subquery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")). subQuery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")).
And(builder.In("label_id", ExcludedLabelIDs.Values())) And(builder.In("label_id", excludedLabelIDs.Values()))
sess.Where(builder.Eq{"0": subquery}) sess.Where(builder.Eq{"0": subQuery})
} }
} }
} }

View File

@ -12,6 +12,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/timeutil" "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 // LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked
func (l *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) { func (l *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) {
labelQuerySlice := []int64{} labelQueryParams := container.Set[string]{}
labelSelected := false labelSelected := false
labelScope := l.ExclusiveScope() exclusiveScope := l.ExclusiveScope()
for i, s := range currentSelectedLabels { for i, curSel := range currentSelectedLabels {
if s == l.ID { if curSel == l.ID {
labelSelected = true labelSelected = true
} else if -s == l.ID { } else if -curSel == l.ID {
labelSelected = true labelSelected = true
l.IsExcluded = true l.IsExcluded = true
} else if s != 0 { } else if curSel != 0 {
// Exclude other labels in the same scope from selection // Exclude other labels in the same scope from selection
if s < 0 || labelScope == "" || labelScope != currentSelectedExclusiveScopes[i] { if curSel < 0 || exclusiveScope == "" || exclusiveScope != currentSelectedExclusiveScopes[i] {
labelQuerySlice = append(labelQuerySlice, s) labelQueryParams.Add(strconv.FormatInt(curSel, 10))
} }
} }
} }
if !labelSelected { if !labelSelected {
labelQuerySlice = append(labelQuerySlice, l.ID) labelQueryParams.Add(strconv.FormatInt(l.ID, 10))
} }
l.IsSelected = labelSelected l.IsSelected = labelSelected
// Sort and deduplicate the ids to avoid the crawlers asking for the // Sort and deduplicate the ids to avoid the crawlers asking for the
// same thing with simply a different order of parameters // same thing with simply a different order of parameters
slices.Sort(labelQuerySlice) labelQuerySliceStrings := labelQueryParams.Values()
labelQuerySlice = slices.Compact(labelQuerySlice) slices.Sort(labelQuerySliceStrings) // the sort is still needed because the underlying map of Set doesn't guarantee order
// 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)
}
l.QueryString = strings.Join(labelQuerySliceStrings, ",") l.QueryString = strings.Join(labelQuerySliceStrings, ",")
} }
@ -187,7 +183,7 @@ func (l *Label) BelongsToRepo() bool {
return l.RepoID > 0 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 { func (l *Label) ExclusiveScope() string {
if !l.Exclusive { if !l.Exclusive {
return "" return ""