[GITEA] Fix the topic search paging
When searching for repository topics, either via the API, or via Explore, paging did not work correctly, because it only applied when the `page` parameter was non-zero. Paging should have applied when the page size is greater than zero, which is what this patch does. As a result, both the API, and the Explore endpoint will return paged results (30 by default). As such, when managing topics on the frontend, the offered completions will also be limited to a pageful of results, based on what the user has already typed. This drastically reduces the amount of traffic, and also the number of the topics to choose from, and thus, the rendering time too. The topics will be returned by popularity, with most used topics first. A single page will contain `[api].DEFAULT_PAGING_NUM` (30 by default) items that match the query. That's plenty to choose from. Fixes #132. Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu> (cherry picked from commit 64d4ff41dbab7b3b84571b595158c3b451f53af7) (cherry picked from commit 06b808fa2c0ddd52ca4569157892a0c7fc154b1f) (cherry picked from commit 9205c9266a7d2b058100d03f5f3272f670f35866) (cherry picked from commit 47863d4f724e7d2465acd6fca91e98157c60a29b)
This commit is contained in:
parent
d1cb590c78
commit
3bdfb7a7aa
3 changed files with 65 additions and 1 deletions
|
@ -199,7 +199,7 @@ func FindTopics(ctx context.Context, opts *FindTopicOptions) ([]*Topic, int64, e
|
||||||
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
|
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
|
||||||
orderBy = "topic.name" // when render topics for a repo, it's better to sort them by name, to get consistent result
|
orderBy = "topic.name" // when render topics for a repo, it's better to sort them by name, to get consistent result
|
||||||
}
|
}
|
||||||
if opts.PageSize != 0 && opts.Page != 0 {
|
if opts.PageSize > 0 {
|
||||||
sess = db.SetSessionPagination(sess, opts)
|
sess = db.SetSessionPagination(sess, opts)
|
||||||
}
|
}
|
||||||
topics := make([]*Topic, 0, 10)
|
topics := make([]*Topic, 0, 10)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package integration
|
package integration
|
||||||
|
@ -19,6 +20,35 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestAPITopicSearchPaging(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
var topics struct {
|
||||||
|
TopicNames []*api.TopicResponse `json:"topics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 20 unique topics to user2/repo2, and 20 unique ones to user2/repo3
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
token2 := getUserToken(t, user2.Name, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
|
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
req := NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/paging-topic-%d", user2.Name, repo2.Name, i).
|
||||||
|
AddTokenAuth(token2)
|
||||||
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/paging-topic-%d", user2.Name, repo3.Name, i+30).
|
||||||
|
AddTokenAuth(token2)
|
||||||
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := MakeRequest(t, NewRequest(t, "GET", "/api/v1/topics/search"), http.StatusOK)
|
||||||
|
DecodeJSON(t, res, &topics)
|
||||||
|
assert.Len(t, topics.TopicNames, 30)
|
||||||
|
|
||||||
|
res = MakeRequest(t, NewRequest(t, "GET", "/api/v1/topics/search?page=2"), http.StatusOK)
|
||||||
|
DecodeJSON(t, res, &topics)
|
||||||
|
assert.Greater(t, len(topics.TopicNames), 0)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPITopicSearch(t *testing.T) {
|
func TestAPITopicSearch(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
searchURL, _ := url.Parse("/api/v1/topics/search")
|
searchURL, _ := url.Parse("/api/v1/topics/search")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package integration
|
package integration
|
||||||
|
@ -8,6 +9,10 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
@ -45,3 +50,32 @@ func TestTopicSearch(t *testing.T) {
|
||||||
assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount)
|
assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTopicSearchPaging(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
var topics struct {
|
||||||
|
TopicNames []*api.TopicResponse `json:"topics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 20 unique topics to user2/repo2, and 20 unique ones to user2/repo3
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
token2 := getUserToken(t, user2.Name, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
|
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
req := NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/paging-topic-%d", user2.Name, repo2.Name, i).
|
||||||
|
AddTokenAuth(token2)
|
||||||
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/paging-topic-%d", user2.Name, repo3.Name, i+30).
|
||||||
|
AddTokenAuth(token2)
|
||||||
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := MakeRequest(t, NewRequest(t, "GET", "/explore/topics/search"), http.StatusOK)
|
||||||
|
DecodeJSON(t, res, &topics)
|
||||||
|
assert.Len(t, topics.TopicNames, 30)
|
||||||
|
|
||||||
|
res = MakeRequest(t, NewRequest(t, "GET", "/explore/topics/search?page=2"), http.StatusOK)
|
||||||
|
DecodeJSON(t, res, &topics)
|
||||||
|
assert.Greater(t, len(topics.TopicNames), 0)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue