mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00: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() | 			likes := builder.NewCond() | ||||||
| 			for _, v := range strings.Split(opts.Keyword, ",") { | 			for _, v := range strings.Split(opts.Keyword, ",") { | ||||||
| 				likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)}) | 				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 { | 				if opts.IncludeDescription { | ||||||
| 					likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)}) | 					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 { | 	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)) | 		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) | 	sess := db.GetEngine(ctx) | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| package models | package models | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"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}, | 			opts:  &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue}, | ||||||
| 			count: 2, | 			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 { | 	for _, testCase := range testCases { | ||||||
| @@ -285,8 +296,22 @@ func TestSearchRepository(t *testing.T) { | |||||||
| 					assert.NotEmpty(t, repo.Name) | 					assert.NotEmpty(t, repo.Name) | ||||||
|  |  | ||||||
| 					if len(testCase.opts.Keyword) > 0 { | 					if len(testCase.opts.Keyword) > 0 { | ||||||
|  | 						// 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) | 							assert.Contains(t, repo.Name, testCase.opts.Keyword) | ||||||
| 						} | 						} | ||||||
|  | 					} | ||||||
|  |  | ||||||
| 					if !testCase.opts.Private { | 					if !testCase.opts.Private { | ||||||
| 						assert.False(t, repo.IsPrivate) | 						assert.False(t, repo.IsPrivate) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user