mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-23 19:47:07 +09:00
Backport #39364 by @shoemoney Sorting by an exclusive label scope joined `issue_label`, returning one row per label. Issues with several labels were listed repeatedly and pushed others off the page, and `IssueIDs` returned inflated totals. The sort key now comes from a correlated subquery, so each issue yields one row. Fixes https://github.com/go-gitea/gitea/issues/35711 This base-branch update was prepared with AI assistance. Co-authored-by: Jeremy Schoemaker <jeremy@shoemoney.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
2 files changed
+26
-5
No files matched your search
@@ -75,11 +75,11 @@ func applySorts(sess db.Session, sortType string, priorityRepoID int64) {
|
||||
// Since this sortType is dynamically created, it has to be treated specially.
|
||||
if after, ok := strings.CutPrefix(sortType, ScopeSortPrefix); ok {
|
||||
scope := after
|
||||
sess.Join("LEFT", "issue_label", "issue.id = issue_label.issue_id")
|
||||
// "exclusive_order=0" means "no order is set", so exclude it from the JOIN criteria and then "LEFT JOIN" result is also null
|
||||
sess.Join("LEFT", "label", "label.id = issue_label.label_id AND label.exclusive_order <> 0 AND label.name LIKE ?", scope+"/%")
|
||||
// Use COALESCE to make sure we sort NULL last regardless of backend DB (2147483647 == max int)
|
||||
sess.OrderBy("COALESCE(label.exclusive_order, 2147483647) ASC").Desc("issue.id")
|
||||
// a subquery instead of a JOIN keeps one row per issue, "exclusive_order=0" means unset and COALESCE sorts it last on every DB
|
||||
sess.OrderBy("COALESCE((SELECT MIN(label.exclusive_order) FROM issue_label"+
|
||||
" INNER JOIN label ON label.id = issue_label.label_id"+
|
||||
" WHERE issue_label.issue_id = issue.id AND label.exclusive_order <> 0 AND label.name LIKE ?), 2147483647) ASC", scope+"/%").
|
||||
Desc("issue.id")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,27 @@ func TestViewIssuesSortByType(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssueIDsSortByLabelScope(t *testing.T) {
|
||||
assert.NoError(t, unittest.LoadFixtures())
|
||||
high := &issues_model.Label{RepoID: 1, Name: "priority/high", Exclusive: true, ExclusiveOrder: 1}
|
||||
low := &issues_model.Label{RepoID: 1, Name: "priority/low", Exclusive: true, ExclusiveOrder: 2}
|
||||
assert.NoError(t, db.Insert(t.Context(), high, low))
|
||||
assert.NoError(t, db.Insert(t.Context(),
|
||||
&issues_model.IssueLabel{IssueID: 1, LabelID: low.ID},
|
||||
&issues_model.IssueLabel{IssueID: 2, LabelID: high.ID},
|
||||
))
|
||||
for page, expectedIDs := range [][]int64{{2, 1, 11}, {5, 3}} {
|
||||
issueIDs, total, err := issues_model.IssueIDs(t.Context(), &issues_model.IssuesOptions{
|
||||
RepoIDs: []int64{1},
|
||||
SortType: issues_model.ScopeSortPrefix + "priority",
|
||||
Paginator: &db.ListOptions{Page: page + 1, PageSize: 3},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedIDs, issueIDs)
|
||||
assert.EqualValues(t, 5, total)
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewIssuesKeyword(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
|
||||
Reference in new issue
Block a user