mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	[Feature] add precise search type for Elastic Search (#12869)
* feat: add type query parameters for specifying precise search * feat: add select dropdown in search box Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
		@@ -280,12 +280,23 @@ func (b *BleveIndexer) Delete(repoID int64) error {
 | 
			
		||||
 | 
			
		||||
// Search searches for files in the specified repo.
 | 
			
		||||
// Returns the matching file-paths
 | 
			
		||||
func (b *BleveIndexer) Search(repoIDs []int64, language, keyword string, page, pageSize int) (int64, []*SearchResult, []*SearchResultLanguages, error) {
 | 
			
		||||
	phraseQuery := bleve.NewMatchPhraseQuery(keyword)
 | 
			
		||||
	phraseQuery.FieldVal = "Content"
 | 
			
		||||
	phraseQuery.Analyzer = repoIndexerAnalyzer
 | 
			
		||||
func (b *BleveIndexer) Search(repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		indexerQuery query.Query
 | 
			
		||||
		keywordQuery query.Query
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if isMatch {
 | 
			
		||||
		prefixQuery := bleve.NewPrefixQuery(keyword)
 | 
			
		||||
		prefixQuery.FieldVal = "Content"
 | 
			
		||||
		keywordQuery = prefixQuery
 | 
			
		||||
	} else {
 | 
			
		||||
		phraseQuery := bleve.NewMatchPhraseQuery(keyword)
 | 
			
		||||
		phraseQuery.FieldVal = "Content"
 | 
			
		||||
		phraseQuery.Analyzer = repoIndexerAnalyzer
 | 
			
		||||
		keywordQuery = phraseQuery
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var indexerQuery query.Query
 | 
			
		||||
	if len(repoIDs) > 0 {
 | 
			
		||||
		var repoQueries = make([]query.Query, 0, len(repoIDs))
 | 
			
		||||
		for _, repoID := range repoIDs {
 | 
			
		||||
@@ -294,10 +305,10 @@ func (b *BleveIndexer) Search(repoIDs []int64, language, keyword string, page, p
 | 
			
		||||
 | 
			
		||||
		indexerQuery = bleve.NewConjunctionQuery(
 | 
			
		||||
			bleve.NewDisjunctionQuery(repoQueries...),
 | 
			
		||||
			phraseQuery,
 | 
			
		||||
			keywordQuery,
 | 
			
		||||
		)
 | 
			
		||||
	} else {
 | 
			
		||||
		indexerQuery = phraseQuery
 | 
			
		||||
		indexerQuery = keywordQuery
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Save for reuse without language filter
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,10 @@ import (
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	esRepoIndexerLatestVersion = 1
 | 
			
		||||
	// multi-match-types, currently only 2 types are used
 | 
			
		||||
	// Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-multi-match-query.html#multi-match-types
 | 
			
		||||
	esMultiMatchTypeBestFields   = "best_fields"
 | 
			
		||||
	esMultiMatchTypePhrasePrefix = "phrase_prefix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
@@ -330,8 +334,13 @@ func extractAggs(searchResult *elastic.SearchResult) []*SearchResultLanguages {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Search searches for codes and language stats by given conditions.
 | 
			
		||||
func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string, page, pageSize int) (int64, []*SearchResult, []*SearchResultLanguages, error) {
 | 
			
		||||
	kwQuery := elastic.NewMultiMatchQuery(keyword, "content")
 | 
			
		||||
func (b *ElasticSearchIndexer) Search(repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error) {
 | 
			
		||||
	searchType := esMultiMatchTypeBestFields
 | 
			
		||||
	if isMatch {
 | 
			
		||||
		searchType = esMultiMatchTypePhrasePrefix
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kwQuery := elastic.NewMultiMatchQuery(keyword, "content").Type(searchType)
 | 
			
		||||
	query := elastic.NewBoolQuery()
 | 
			
		||||
	query = query.Must(kwQuery)
 | 
			
		||||
	if len(repoIDs) > 0 {
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ type SearchResultLanguages struct {
 | 
			
		||||
type Indexer interface {
 | 
			
		||||
	Index(repo *models.Repository, sha string, changes *repoChanges) error
 | 
			
		||||
	Delete(repoID int64) error
 | 
			
		||||
	Search(repoIDs []int64, language, keyword string, page, pageSize int) (int64, []*SearchResult, []*SearchResultLanguages, error)
 | 
			
		||||
	Search(repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error)
 | 
			
		||||
	Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ func testIndexer(name string, t *testing.T, indexer Indexer) {
 | 
			
		||||
 | 
			
		||||
		for _, kw := range keywords {
 | 
			
		||||
			t.Run(kw.Keyword, func(t *testing.T) {
 | 
			
		||||
				total, res, langs, err := indexer.Search(kw.RepoIDs, "", kw.Keyword, 1, 10)
 | 
			
		||||
				total, res, langs, err := indexer.Search(kw.RepoIDs, "", kw.Keyword, 1, 10, false)
 | 
			
		||||
				assert.NoError(t, err)
 | 
			
		||||
				assert.EqualValues(t, len(kw.IDs), total)
 | 
			
		||||
				assert.EqualValues(t, kw.Langs, len(langs))
 | 
			
		||||
 
 | 
			
		||||
@@ -106,12 +106,12 @@ func searchResult(result *SearchResult, startIndex, endIndex int) (*Result, erro
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PerformSearch perform a search on a repository
 | 
			
		||||
func PerformSearch(repoIDs []int64, language, keyword string, page, pageSize int) (int, []*Result, []*SearchResultLanguages, error) {
 | 
			
		||||
func PerformSearch(repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int, []*Result, []*SearchResultLanguages, error) {
 | 
			
		||||
	if len(keyword) == 0 {
 | 
			
		||||
		return 0, nil, nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	total, results, resultLanguages, err := indexer.Search(repoIDs, language, keyword, page, pageSize)
 | 
			
		||||
	total, results, resultLanguages, err := indexer.Search(repoIDs, language, keyword, page, pageSize, isMatch)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -73,12 +73,12 @@ func (w *wrappedIndexer) Delete(repoID int64) error {
 | 
			
		||||
	return indexer.Delete(repoID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *wrappedIndexer) Search(repoIDs []int64, language, keyword string, page, pageSize int) (int64, []*SearchResult, []*SearchResultLanguages, error) {
 | 
			
		||||
func (w *wrappedIndexer) Search(repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error) {
 | 
			
		||||
	indexer, err := w.get()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return indexer.Search(repoIDs, language, keyword, page, pageSize)
 | 
			
		||||
	return indexer.Search(repoIDs, language, keyword, page, pageSize, isMatch)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user