mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Allows repo search to match against "owner/repo" pattern strings (#19754)
* Allows repo search to match against "owner/repo" pattern strings * Gofumpt * Adds test case for "owner/repo" style repo search * With "owner/repo" search terms, prioritise results which match the owner field * Fixes unquoted SQL string in repo search
This commit is contained in:
		@@ -459,6 +459,15 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
 | 
			
		||||
			likes := builder.NewCond()
 | 
			
		||||
			for _, v := range strings.Split(opts.Keyword, ",") {
 | 
			
		||||
				likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
 | 
			
		||||
 | 
			
		||||
				// If the string looks like "org/repo", match against that pattern too
 | 
			
		||||
				if opts.TeamID == 0 && strings.Count(opts.Keyword, "/") == 1 {
 | 
			
		||||
					pieces := strings.Split(opts.Keyword, "/")
 | 
			
		||||
					ownerName := pieces[0]
 | 
			
		||||
					repoName := pieces[1]
 | 
			
		||||
					likes = likes.Or(builder.And(builder.Like{"owner_name", strings.ToLower(ownerName)}, builder.Like{"lower_name", strings.ToLower(repoName)}))
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if opts.IncludeDescription {
 | 
			
		||||
					likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
 | 
			
		||||
				}
 | 
			
		||||
@@ -549,6 +558,10 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
 | 
			
		||||
 | 
			
		||||
	if opts.PriorityOwnerID > 0 {
 | 
			
		||||
		opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = %d THEN 0 ELSE owner_id END, %s", opts.PriorityOwnerID, opts.OrderBy))
 | 
			
		||||
	} else if strings.Count(opts.Keyword, "/") == 1 {
 | 
			
		||||
		// With "owner/repo" search times, prioritise results which match the owner field
 | 
			
		||||
		orgName := strings.Split(opts.Keyword, "/")[0]
 | 
			
		||||
		opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_name LIKE '%s' THEN 0 ELSE 1 END, %s", orgName, opts.OrderBy))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sess := db.GetEngine(ctx)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models/db"
 | 
			
		||||
@@ -261,6 +262,16 @@ func TestSearchRepository(t *testing.T) {
 | 
			
		||||
			opts:  &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue},
 | 
			
		||||
			count: 2,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:  "OwnerSlashRepoSearch",
 | 
			
		||||
			opts:  &SearchRepoOptions{Keyword: "user/repo2", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
 | 
			
		||||
			count: 3,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:  "OwnerSlashSearch",
 | 
			
		||||
			opts:  &SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
 | 
			
		||||
			count: 4,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, testCase := range testCases {
 | 
			
		||||
@@ -285,7 +296,21 @@ func TestSearchRepository(t *testing.T) {
 | 
			
		||||
					assert.NotEmpty(t, repo.Name)
 | 
			
		||||
 | 
			
		||||
					if len(testCase.opts.Keyword) > 0 {
 | 
			
		||||
						assert.Contains(t, repo.Name, testCase.opts.Keyword)
 | 
			
		||||
						// Keyword match condition is different for search terms of form "owner/repo"
 | 
			
		||||
						if strings.Count(testCase.opts.Keyword, "/") == 1 {
 | 
			
		||||
							// May still match as a whole...
 | 
			
		||||
							wholeMatch := strings.Contains(repo.Name, testCase.opts.Keyword)
 | 
			
		||||
 | 
			
		||||
							pieces := strings.Split(testCase.opts.Keyword, "/")
 | 
			
		||||
							ownerName := pieces[0]
 | 
			
		||||
							repoName := pieces[1]
 | 
			
		||||
							// ... or match in parts
 | 
			
		||||
							splitMatch := strings.Contains(repo.OwnerName, ownerName) && strings.Contains(repo.Name, repoName)
 | 
			
		||||
 | 
			
		||||
							assert.True(t, wholeMatch || splitMatch, "Keyword '%s' does not match repo '%s/%s'", testCase.opts.Keyword, repo.Owner.Name, repo.Name)
 | 
			
		||||
						} else {
 | 
			
		||||
							assert.Contains(t, repo.Name, testCase.opts.Keyword)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if !testCase.opts.Private {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user