mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	Include description in repository search. (#7942)
* Add description in repository search. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Refactor SearchRepositoryByName with a general function SearchRepository Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Allow to specify if description shall be included in API repo search. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Add new app.ini setting for whether to search within repo description. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Search keyword in description (if setting enabled) on: - Explore page - Organization profile page - User profile page - Admin repo page Do not search keyword in description on: - Any non-keyword search (not relevant) - Incremental search (uses API) Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Put parameters related to keyword directly after it Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Add test cases for including (and not including) repository description in search. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Rename test function from TestSearchRepositoryByName to TestSearchRepository. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Make setting SEARCH_REPO_DESCRIPTION default to true Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
This commit is contained in:
		
				
					committed by
					
						
						Lauris BH
					
				
			
			
				
	
			
			
			
						parent
						
							8c24bb9e43
						
					
				
				
					commit
					c9546d4cdd
				
			@@ -116,6 +116,8 @@ DEFAULT_THEME = gitea
 | 
			
		||||
THEMES = gitea,arc-green
 | 
			
		||||
; Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
 | 
			
		||||
DEFAULT_SHOW_FULL_NAME = false
 | 
			
		||||
; Whether to search within description at repository search on explore page.
 | 
			
		||||
SEARCH_REPO_DESCRIPTION = true
 | 
			
		||||
 | 
			
		||||
[ui.admin]
 | 
			
		||||
; Number of users that are displayed on one page
 | 
			
		||||
 
 | 
			
		||||
@@ -96,6 +96,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 | 
			
		||||
- `THEMES`:  **gitea,arc-green**: All available themes. Allow users select personalized themes
 | 
			
		||||
  regardless of the value of `DEFAULT_THEME`.
 | 
			
		||||
- `DEFAULT_SHOW_FULL_NAME`: false: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
 | 
			
		||||
- `SEARCH_REPO_DESCRIPTION`: true: Whether to search within description at repository search on explore page.
 | 
			
		||||
 | 
			
		||||
### UI - Admin (`ui.admin`)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -165,6 +165,7 @@
 | 
			
		||||
  owner_id: 14
 | 
			
		||||
  lower_name: test_repo_14
 | 
			
		||||
  name: test_repo_14
 | 
			
		||||
  description: test_description_14
 | 
			
		||||
  is_private: false
 | 
			
		||||
  num_issues: 0
 | 
			
		||||
  num_closed_issues: 0
 | 
			
		||||
 
 | 
			
		||||
@@ -136,6 +136,8 @@ type SearchRepoOptions struct {
 | 
			
		||||
	Mirror util.OptionalBool
 | 
			
		||||
	// only search topic name
 | 
			
		||||
	TopicOnly bool
 | 
			
		||||
	// include description in keyword search
 | 
			
		||||
	IncludeDescription bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//SearchOrderBy is used to sort the result
 | 
			
		||||
@@ -163,9 +165,9 @@ const (
 | 
			
		||||
	SearchOrderByForksReverse          SearchOrderBy = "num_forks DESC"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SearchRepositoryByName takes keyword and part of repository name to search,
 | 
			
		||||
// SearchRepository returns repositories based on search options,
 | 
			
		||||
// it returns results in given range and number of total results.
 | 
			
		||||
func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
			
		||||
func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
			
		||||
	if opts.Page <= 0 {
 | 
			
		||||
		opts.Page = 1
 | 
			
		||||
	}
 | 
			
		||||
@@ -264,6 +266,9 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
 | 
			
		||||
			var likes = builder.NewCond()
 | 
			
		||||
			for _, v := range strings.Split(opts.Keyword, ",") {
 | 
			
		||||
				likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
 | 
			
		||||
				if opts.IncludeDescription {
 | 
			
		||||
					likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			keywordCond = keywordCond.Or(likes)
 | 
			
		||||
		}
 | 
			
		||||
@@ -311,6 +316,13 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
 | 
			
		||||
	return repos, count, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SearchRepositoryByName takes keyword and part of repository name to search,
 | 
			
		||||
// it returns results in given range and number of total results.
 | 
			
		||||
func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
			
		||||
	opts.IncludeDescription = false
 | 
			
		||||
	return SearchRepository(opts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
 | 
			
		||||
func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
 | 
			
		||||
	var accessCond builder.Cond = builder.Eq{"is_private": false}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSearchRepositoryByName(t *testing.T) {
 | 
			
		||||
func TestSearchRepository(t *testing.T) {
 | 
			
		||||
	assert.NoError(t, PrepareTestDatabase())
 | 
			
		||||
 | 
			
		||||
	// test search public repository on explore page
 | 
			
		||||
@@ -74,6 +74,34 @@ func TestSearchRepositoryByName(t *testing.T) {
 | 
			
		||||
	assert.Empty(t, repos)
 | 
			
		||||
	assert.Equal(t, int64(0), count)
 | 
			
		||||
 | 
			
		||||
	// Test search within description
 | 
			
		||||
	repos, count, err = SearchRepository(&SearchRepoOptions{
 | 
			
		||||
		Keyword:            "description_14",
 | 
			
		||||
		Page:               1,
 | 
			
		||||
		PageSize:           10,
 | 
			
		||||
		Collaborate:        util.OptionalBoolFalse,
 | 
			
		||||
		IncludeDescription: true,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	if assert.Len(t, repos, 1) {
 | 
			
		||||
		assert.Equal(t, "test_repo_14", repos[0].Name)
 | 
			
		||||
	}
 | 
			
		||||
	assert.Equal(t, int64(1), count)
 | 
			
		||||
 | 
			
		||||
	// Test NOT search within description
 | 
			
		||||
	repos, count, err = SearchRepository(&SearchRepoOptions{
 | 
			
		||||
		Keyword:            "description_14",
 | 
			
		||||
		Page:               1,
 | 
			
		||||
		PageSize:           10,
 | 
			
		||||
		Collaborate:        util.OptionalBoolFalse,
 | 
			
		||||
		IncludeDescription: false,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Empty(t, repos)
 | 
			
		||||
	assert.Equal(t, int64(0), count)
 | 
			
		||||
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name  string
 | 
			
		||||
		opts  *SearchRepoOptions
 | 
			
		||||
 
 | 
			
		||||
@@ -163,6 +163,7 @@ var (
 | 
			
		||||
		DefaultShowFullName   bool
 | 
			
		||||
		DefaultTheme          string
 | 
			
		||||
		Themes                []string
 | 
			
		||||
		SearchRepoDescription bool
 | 
			
		||||
 | 
			
		||||
		Admin struct {
 | 
			
		||||
			UserPagingNum   int
 | 
			
		||||
@@ -942,6 +943,7 @@ func NewContext() {
 | 
			
		||||
 | 
			
		||||
	UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
 | 
			
		||||
	UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
 | 
			
		||||
	UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
 | 
			
		||||
 | 
			
		||||
	HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,10 @@ func Search(ctx *context.APIContext) {
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: Limit search to repositories with keyword as topic
 | 
			
		||||
	//   type: boolean
 | 
			
		||||
	// - name: includeDesc
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: include search of keyword within repository description
 | 
			
		||||
	//   type: boolean
 | 
			
		||||
	// - name: uid
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: search only for repos that the user with the given id owns or contributes to
 | 
			
		||||
@@ -113,6 +117,7 @@ func Search(ctx *context.APIContext) {
 | 
			
		||||
		UserIsAdmin:        ctx.IsUserSiteAdmin(),
 | 
			
		||||
		UserID:             ctx.Data["SignedUserID"].(int64),
 | 
			
		||||
		StarredByID:        ctx.QueryInt64("starredBy"),
 | 
			
		||||
		IncludeDescription: ctx.QueryBool("includeDesc"),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctx.QueryBool("exclusive") {
 | 
			
		||||
@@ -157,7 +162,7 @@ func Search(ctx *context.APIContext) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	repos, count, err := models.SearchRepositoryByName(opts)
 | 
			
		||||
	repos, count, err := models.SearchRepository(opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.JSON(500, api.SearchError{
 | 
			
		||||
			OK:    false,
 | 
			
		||||
 
 | 
			
		||||
@@ -133,7 +133,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
 | 
			
		||||
	keyword := strings.Trim(ctx.Query("q"), " ")
 | 
			
		||||
	topicOnly := ctx.QueryBool("topic")
 | 
			
		||||
 | 
			
		||||
	repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
 | 
			
		||||
	repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
 | 
			
		||||
		Page:               page,
 | 
			
		||||
		PageSize:           opts.PageSize,
 | 
			
		||||
		OrderBy:            orderBy,
 | 
			
		||||
@@ -142,9 +142,10 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
 | 
			
		||||
		OwnerID:            opts.OwnerID,
 | 
			
		||||
		AllPublic:          true,
 | 
			
		||||
		TopicOnly:          topicOnly,
 | 
			
		||||
		IncludeDescription: setting.UI.SearchRepoDescription,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("SearchRepositoryByName", err)
 | 
			
		||||
		ctx.ServerError("SearchRepository", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data["Keyword"] = keyword
 | 
			
		||||
 
 | 
			
		||||
@@ -499,7 +499,7 @@ func showOrgProfile(ctx *context.Context) {
 | 
			
		||||
		count int64
 | 
			
		||||
		err   error
 | 
			
		||||
	)
 | 
			
		||||
	repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
 | 
			
		||||
	repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
 | 
			
		||||
		Keyword:            keyword,
 | 
			
		||||
		OwnerID:            org.ID,
 | 
			
		||||
		OrderBy:            orderBy,
 | 
			
		||||
@@ -509,9 +509,10 @@ func showOrgProfile(ctx *context.Context) {
 | 
			
		||||
		Page:               page,
 | 
			
		||||
		IsProfile:          true,
 | 
			
		||||
		PageSize:           setting.UI.User.RepoPagingNum,
 | 
			
		||||
		IncludeDescription: setting.UI.SearchRepoDescription,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("SearchRepositoryByName", err)
 | 
			
		||||
		ctx.ServerError("SearchRepository", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -169,7 +169,7 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
		}
 | 
			
		||||
	case "stars":
 | 
			
		||||
		ctx.Data["PageIsProfileStarList"] = true
 | 
			
		||||
		repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
 | 
			
		||||
		repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
 | 
			
		||||
			Keyword:            keyword,
 | 
			
		||||
			OrderBy:            orderBy,
 | 
			
		||||
			Private:            ctx.IsSigned,
 | 
			
		||||
@@ -180,15 +180,16 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
			StarredByID:        ctxUser.ID,
 | 
			
		||||
			Collaborate:        util.OptionalBoolFalse,
 | 
			
		||||
			TopicOnly:          topicOnly,
 | 
			
		||||
			IncludeDescription: setting.UI.SearchRepoDescription,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.ServerError("SearchRepositoryByName", err)
 | 
			
		||||
			ctx.ServerError("SearchRepository", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		total = int(count)
 | 
			
		||||
	default:
 | 
			
		||||
		repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
 | 
			
		||||
		repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
 | 
			
		||||
			Keyword:            keyword,
 | 
			
		||||
			OwnerID:            ctxUser.ID,
 | 
			
		||||
			OrderBy:            orderBy,
 | 
			
		||||
@@ -200,9 +201,10 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
			PageSize:           setting.UI.User.RepoPagingNum,
 | 
			
		||||
			Collaborate:        util.OptionalBoolFalse,
 | 
			
		||||
			TopicOnly:          topicOnly,
 | 
			
		||||
			IncludeDescription: setting.UI.SearchRepoDescription,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.ServerError("SearchRepositoryByName", err)
 | 
			
		||||
			ctx.ServerError("SearchRepository", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1099,6 +1099,12 @@
 | 
			
		||||
            "name": "topic",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "boolean",
 | 
			
		||||
            "description": "include search of keyword within repository description",
 | 
			
		||||
            "name": "includeDesc",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
            "format": "int64",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user