Skip to content

Commit 036deae

Browse files
committed
issue_search: optimize database queries on LabelIDs
1 parent f5428ac commit 036deae

File tree

1 file changed

+21
-7
lines changed

1 file changed

+21
-7
lines changed

models/issues/issue_search.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package issues
66
import (
77
"context"
88
"fmt"
9+
"strconv"
910
"strings"
1011

1112
"code.gitea.io/gitea/models/db"
@@ -130,17 +131,30 @@ func applyLabelsCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.Session
130131
if opts.LabelIDs[0] == 0 {
131132
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)")
132133
} else {
133-
// We deduplicate the ID to reduce the load the database
134-
// (possible DoS here by multiplying the joins)
135-
uniqueLabelIDs := container.SetOf(opts.LabelIDs...).Values()
136-
for i, labelID := range uniqueLabelIDs {
134+
// We sort and deduplicate the labels' ids
135+
IncludedLabelIDs := make(container.Set[int64])
136+
ExcludedLabelIDs := make(container.Set[int64])
137+
for _, labelID := range opts.LabelIDs {
137138
if labelID > 0 {
138-
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
139-
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
139+
IncludedLabelIDs.Add(labelID)
140140
} else if labelID < 0 { // 0 is not supported here, so just ignore it
141-
sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID)
141+
ExcludedLabelIDs.Add(-labelID)
142142
}
143143
}
144+
// ... and use them in a subquery of the form :
145+
// where (select count(*) from issue_label where issue_id=issue.id and label_id in (2, 4, 6)) = 3
146+
// This equality is garanteed thanks to unique index (issue_id,label_id) on table issue_label.
147+
if (len(IncludedLabelIDs) > 0) {
148+
subquery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")).
149+
And(builder.In("label_id", IncludedLabelIDs.Values()))
150+
sess.Where(builder.Eq{strconv.Itoa(len(IncludedLabelIDs)): subquery})
151+
}
152+
// or (select count(*)...) = 0 for excluded labels
153+
if (len(ExcludedLabelIDs) > 0) {
154+
subquery := builder.Select("count(*)").From("issue_label").Where(builder.Expr("issue_id = issue.id")).
155+
And(builder.In("label_id", ExcludedLabelIDs.Values()))
156+
sess.Where(builder.Eq{"0": subquery})
157+
}
144158
}
145159
}
146160

0 commit comments

Comments
 (0)