mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	refactor issue indexer, add some testing and fix a bug (#6131)
* refactor issue indexer, add some testing and fix a bug * fix error copyright year on comment header * issues indexer package import keep consistent
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -62,6 +62,7 @@ coverage.all
 | 
				
			|||||||
/integrations/pgsql.ini
 | 
					/integrations/pgsql.ini
 | 
				
			||||||
/integrations/mssql.ini
 | 
					/integrations/mssql.ini
 | 
				
			||||||
/node_modules
 | 
					/node_modules
 | 
				
			||||||
 | 
					/modules/indexer/issues/indexers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Snapcraft
 | 
					# Snapcraft
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1231,6 +1231,11 @@ func getIssueIDsByRepoID(e Engine, repoID int64) ([]int64, error) {
 | 
				
			|||||||
	return ids, err
 | 
						return ids, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetIssueIDsByRepoID returns all issue ids by repo id
 | 
				
			||||||
 | 
					func GetIssueIDsByRepoID(repoID int64) ([]int64, error) {
 | 
				
			||||||
 | 
						return getIssueIDsByRepoID(x, repoID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetIssuesByIDs return issues with the given IDs.
 | 
					// GetIssuesByIDs return issues with the given IDs.
 | 
				
			||||||
func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
 | 
					func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
 | 
				
			||||||
	return getIssuesByIDs(x, issueIDs)
 | 
						return getIssuesByIDs(x, issueIDs)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,148 +0,0 @@
 | 
				
			|||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
					 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package models
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/indexer/issues"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	// issueIndexerUpdateQueue queue of issue ids to be updated
 | 
					 | 
				
			||||||
	issueIndexerUpdateQueue issues.Queue
 | 
					 | 
				
			||||||
	issueIndexer            issues.Indexer
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// InitIssueIndexer initialize issue indexer
 | 
					 | 
				
			||||||
func InitIssueIndexer() error {
 | 
					 | 
				
			||||||
	var populate bool
 | 
					 | 
				
			||||||
	switch setting.Indexer.IssueType {
 | 
					 | 
				
			||||||
	case "bleve":
 | 
					 | 
				
			||||||
		issueIndexer = issues.NewBleveIndexer(setting.Indexer.IssuePath)
 | 
					 | 
				
			||||||
		exist, err := issueIndexer.Init()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		populate = !exist
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return fmt.Errorf("unknow issue indexer type: %s", setting.Indexer.IssueType)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	switch setting.Indexer.IssueIndexerQueueType {
 | 
					 | 
				
			||||||
	case setting.LevelQueueType:
 | 
					 | 
				
			||||||
		issueIndexerUpdateQueue, err = issues.NewLevelQueue(
 | 
					 | 
				
			||||||
			issueIndexer,
 | 
					 | 
				
			||||||
			setting.Indexer.IssueIndexerQueueDir,
 | 
					 | 
				
			||||||
			setting.Indexer.IssueIndexerQueueBatchNumber)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case setting.ChannelQueueType:
 | 
					 | 
				
			||||||
		issueIndexerUpdateQueue = issues.NewChannelQueue(issueIndexer, setting.Indexer.IssueIndexerQueueBatchNumber)
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return fmt.Errorf("Unsupported indexer queue type: %v", setting.Indexer.IssueIndexerQueueType)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	go issueIndexerUpdateQueue.Run()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if populate {
 | 
					 | 
				
			||||||
		go populateIssueIndexer()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// populateIssueIndexer populate the issue indexer with issue data
 | 
					 | 
				
			||||||
func populateIssueIndexer() {
 | 
					 | 
				
			||||||
	for page := 1; ; page++ {
 | 
					 | 
				
			||||||
		repos, _, err := SearchRepositoryByName(&SearchRepoOptions{
 | 
					 | 
				
			||||||
			Page:        page,
 | 
					 | 
				
			||||||
			PageSize:    RepositoryListDefaultPageSize,
 | 
					 | 
				
			||||||
			OrderBy:     SearchOrderByID,
 | 
					 | 
				
			||||||
			Private:     true,
 | 
					 | 
				
			||||||
			Collaborate: util.OptionalBoolFalse,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error(4, "SearchRepositoryByName: %v", err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(repos) == 0 {
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for _, repo := range repos {
 | 
					 | 
				
			||||||
			is, err := Issues(&IssuesOptions{
 | 
					 | 
				
			||||||
				RepoIDs:  []int64{repo.ID},
 | 
					 | 
				
			||||||
				IsClosed: util.OptionalBoolNone,
 | 
					 | 
				
			||||||
				IsPull:   util.OptionalBoolNone,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Error(4, "Issues: %v", err)
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if err = IssueList(is).LoadDiscussComments(); err != nil {
 | 
					 | 
				
			||||||
				log.Error(4, "LoadComments: %v", err)
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			for _, issue := range is {
 | 
					 | 
				
			||||||
				UpdateIssueIndexer(issue)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UpdateIssueIndexer add/update an issue to the issue indexer
 | 
					 | 
				
			||||||
func UpdateIssueIndexer(issue *Issue) {
 | 
					 | 
				
			||||||
	var comments []string
 | 
					 | 
				
			||||||
	for _, comment := range issue.Comments {
 | 
					 | 
				
			||||||
		if comment.Type == CommentTypeComment {
 | 
					 | 
				
			||||||
			comments = append(comments, comment.Content)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	issueIndexerUpdateQueue.Push(&issues.IndexerData{
 | 
					 | 
				
			||||||
		ID:       issue.ID,
 | 
					 | 
				
			||||||
		RepoID:   issue.RepoID,
 | 
					 | 
				
			||||||
		Title:    issue.Title,
 | 
					 | 
				
			||||||
		Content:  issue.Content,
 | 
					 | 
				
			||||||
		Comments: comments,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteRepoIssueIndexer deletes repo's all issues indexes
 | 
					 | 
				
			||||||
func DeleteRepoIssueIndexer(repo *Repository) {
 | 
					 | 
				
			||||||
	var ids []int64
 | 
					 | 
				
			||||||
	ids, err := getIssueIDsByRepoID(x, repo.ID)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Error(4, "getIssueIDsByRepoID failed: %v", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(ids) <= 0 {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	issueIndexerUpdateQueue.Push(&issues.IndexerData{
 | 
					 | 
				
			||||||
		IDs:      ids,
 | 
					 | 
				
			||||||
		IsDelete: true,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SearchIssuesByKeyword search issue ids by keywords and repo id
 | 
					 | 
				
			||||||
func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) {
 | 
					 | 
				
			||||||
	var issueIDs []int64
 | 
					 | 
				
			||||||
	res, err := issueIndexer.Search(keyword, repoID, 1000, 0)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, r := range res.Hits {
 | 
					 | 
				
			||||||
		issueIDs = append(issueIDs, r.ID)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return issueIDs, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -44,10 +44,6 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
 | 
				
			|||||||
		fatalTestError("Error creating test engine: %v\n", err)
 | 
							fatalTestError("Error creating test engine: %v\n", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = InitIssueIndexer(); err != nil {
 | 
					 | 
				
			||||||
		fatalTestError("Error InitIssueIndexer: %v\n", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setting.AppURL = "https://try.gitea.io/"
 | 
						setting.AppURL = "https://try.gitea.io/"
 | 
				
			||||||
	setting.RunUser = "runuser"
 | 
						setting.RunUser = "runuser"
 | 
				
			||||||
	setting.SSH.Port = 3000
 | 
						setting.SSH.Port = 3000
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package issues
 | 
					package issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IndexerData data stored in the issue indexer
 | 
					// IndexerData data stored in the issue indexer
 | 
				
			||||||
type IndexerData struct {
 | 
					type IndexerData struct {
 | 
				
			||||||
	ID       int64
 | 
						ID       int64
 | 
				
			||||||
@@ -34,3 +43,142 @@ type Indexer interface {
 | 
				
			|||||||
	Delete(ids ...int64) error
 | 
						Delete(ids ...int64) error
 | 
				
			||||||
	Search(kw string, repoID int64, limit, start int) (*SearchResult, error)
 | 
						Search(kw string, repoID int64, limit, start int) (*SearchResult, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// issueIndexerUpdateQueue queue of issue ids to be updated
 | 
				
			||||||
 | 
						issueIndexerUpdateQueue Queue
 | 
				
			||||||
 | 
						issueIndexer            Indexer
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
 | 
				
			||||||
 | 
					// all issue index done.
 | 
				
			||||||
 | 
					func InitIssueIndexer(syncReindex bool) error {
 | 
				
			||||||
 | 
						var populate bool
 | 
				
			||||||
 | 
						switch setting.Indexer.IssueType {
 | 
				
			||||||
 | 
						case "bleve":
 | 
				
			||||||
 | 
							issueIndexer = NewBleveIndexer(setting.Indexer.IssuePath)
 | 
				
			||||||
 | 
							exist, err := issueIndexer.Init()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							populate = !exist
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("unknow issue indexer type: %s", setting.Indexer.IssueType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						switch setting.Indexer.IssueIndexerQueueType {
 | 
				
			||||||
 | 
						case setting.LevelQueueType:
 | 
				
			||||||
 | 
							issueIndexerUpdateQueue, err = NewLevelQueue(
 | 
				
			||||||
 | 
								issueIndexer,
 | 
				
			||||||
 | 
								setting.Indexer.IssueIndexerQueueDir,
 | 
				
			||||||
 | 
								setting.Indexer.IssueIndexerQueueBatchNumber)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case setting.ChannelQueueType:
 | 
				
			||||||
 | 
							issueIndexerUpdateQueue = NewChannelQueue(issueIndexer, setting.Indexer.IssueIndexerQueueBatchNumber)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("Unsupported indexer queue type: %v", setting.Indexer.IssueIndexerQueueType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go issueIndexerUpdateQueue.Run()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if populate {
 | 
				
			||||||
 | 
							if syncReindex {
 | 
				
			||||||
 | 
								populateIssueIndexer()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								go populateIssueIndexer()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// populateIssueIndexer populate the issue indexer with issue data
 | 
				
			||||||
 | 
					func populateIssueIndexer() {
 | 
				
			||||||
 | 
						for page := 1; ; page++ {
 | 
				
			||||||
 | 
							repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
 | 
				
			||||||
 | 
								Page:        page,
 | 
				
			||||||
 | 
								PageSize:    models.RepositoryListDefaultPageSize,
 | 
				
			||||||
 | 
								OrderBy:     models.SearchOrderByID,
 | 
				
			||||||
 | 
								Private:     true,
 | 
				
			||||||
 | 
								Collaborate: util.OptionalBoolFalse,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Error(4, "SearchRepositoryByName: %v", err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(repos) == 0 {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, repo := range repos {
 | 
				
			||||||
 | 
								is, err := models.Issues(&models.IssuesOptions{
 | 
				
			||||||
 | 
									RepoIDs:  []int64{repo.ID},
 | 
				
			||||||
 | 
									IsClosed: util.OptionalBoolNone,
 | 
				
			||||||
 | 
									IsPull:   util.OptionalBoolNone,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Error(4, "Issues: %v", err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err = models.IssueList(is).LoadDiscussComments(); err != nil {
 | 
				
			||||||
 | 
									log.Error(4, "LoadComments: %v", err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for _, issue := range is {
 | 
				
			||||||
 | 
									UpdateIssueIndexer(issue)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateIssueIndexer add/update an issue to the issue indexer
 | 
				
			||||||
 | 
					func UpdateIssueIndexer(issue *models.Issue) {
 | 
				
			||||||
 | 
						var comments []string
 | 
				
			||||||
 | 
						for _, comment := range issue.Comments {
 | 
				
			||||||
 | 
							if comment.Type == models.CommentTypeComment {
 | 
				
			||||||
 | 
								comments = append(comments, comment.Content)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						issueIndexerUpdateQueue.Push(&IndexerData{
 | 
				
			||||||
 | 
							ID:       issue.ID,
 | 
				
			||||||
 | 
							RepoID:   issue.RepoID,
 | 
				
			||||||
 | 
							Title:    issue.Title,
 | 
				
			||||||
 | 
							Content:  issue.Content,
 | 
				
			||||||
 | 
							Comments: comments,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteRepoIssueIndexer deletes repo's all issues indexes
 | 
				
			||||||
 | 
					func DeleteRepoIssueIndexer(repo *models.Repository) {
 | 
				
			||||||
 | 
						var ids []int64
 | 
				
			||||||
 | 
						ids, err := models.GetIssueIDsByRepoID(repo.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Error(4, "getIssueIDsByRepoID failed: %v", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(ids) <= 0 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						issueIndexerUpdateQueue.Push(&IndexerData{
 | 
				
			||||||
 | 
							IDs:      ids,
 | 
				
			||||||
 | 
							IsDelete: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchIssuesByKeyword search issue ids by keywords and repo id
 | 
				
			||||||
 | 
					func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) {
 | 
				
			||||||
 | 
						var issueIDs []int64
 | 
				
			||||||
 | 
						res, err := issueIndexer.Search(keyword, repoID, 1000, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, r := range res.Hits {
 | 
				
			||||||
 | 
							issueIDs = append(issueIDs, r.ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return issueIDs, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										51
									
								
								modules/indexer/issues/indexer_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								modules/indexer/issues/indexer_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func fatalTestError(fmtStr string, args ...interface{}) {
 | 
				
			||||||
 | 
						fmt.Fprintf(os.Stderr, fmtStr, args...)
 | 
				
			||||||
 | 
						os.Exit(1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMain(m *testing.M) {
 | 
				
			||||||
 | 
						models.MainTest(m, filepath.Join("..", "..", ".."))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSearchIssues(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, models.PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os.RemoveAll(setting.Indexer.IssueIndexerQueueDir)
 | 
				
			||||||
 | 
						os.RemoveAll(setting.Indexer.IssuePath)
 | 
				
			||||||
 | 
						if err := InitIssueIndexer(true); err != nil {
 | 
				
			||||||
 | 
							fatalTestError("Error InitIssueIndexer: %v\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(10 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ids, err := SearchIssuesByKeyword(1, "issue2")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.EqualValues(t, []int64{2}, ids)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ids, err = SearchIssuesByKeyword(1, "first")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.EqualValues(t, []int64{1}, ids)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ids, err = SearchIssuesByKeyword(1, "for")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.EqualValues(t, []int64{1, 2, 3, 5}, ids)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -42,18 +42,21 @@ func (l *LevelQueue) Run() error {
 | 
				
			|||||||
	var i int
 | 
						var i int
 | 
				
			||||||
	var datas = make([]*IndexerData, 0, l.batchNumber)
 | 
						var datas = make([]*IndexerData, 0, l.batchNumber)
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		bs, err := l.queue.RPop()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error(4, "RPop: %v", err)
 | 
					 | 
				
			||||||
			time.Sleep(time.Millisecond * 100)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		i++
 | 
							i++
 | 
				
			||||||
		if len(datas) > l.batchNumber || (len(datas) > 0 && i > 3) {
 | 
							if len(datas) > l.batchNumber || (len(datas) > 0 && i > 3) {
 | 
				
			||||||
			l.indexer.Index(datas)
 | 
								l.indexer.Index(datas)
 | 
				
			||||||
			datas = make([]*IndexerData, 0, l.batchNumber)
 | 
								datas = make([]*IndexerData, 0, l.batchNumber)
 | 
				
			||||||
			i = 0
 | 
								i = 0
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bs, err := l.queue.RPop()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if err != levelqueue.ErrNotFound {
 | 
				
			||||||
 | 
									log.Error(4, "RPop: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								time.Sleep(time.Millisecond * 100)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if len(bs) <= 0 {
 | 
							if len(bs) <= 0 {
 | 
				
			||||||
@@ -69,7 +72,7 @@ func (l *LevelQueue) Run() error {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Trace("LedisLocalQueue: task found: %#v", data)
 | 
							log.Trace("LevelQueue: task found: %#v", data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if data.IsDelete {
 | 
							if data.IsDelete {
 | 
				
			||||||
			if data.ID > 0 {
 | 
								if data.ID > 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ package indexer
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/notification/base"
 | 
						"code.gitea.io/gitea/modules/notification/base"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -35,16 +36,16 @@ func (r *indexerNotifier) NotifyCreateIssueComment(doer *models.User, repo *mode
 | 
				
			|||||||
			issue.Comments = append(issue.Comments, comment)
 | 
								issue.Comments = append(issue.Comments, comment)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		models.UpdateIssueIndexer(issue)
 | 
							issue_indexer.UpdateIssueIndexer(issue)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *indexerNotifier) NotifyNewIssue(issue *models.Issue) {
 | 
					func (r *indexerNotifier) NotifyNewIssue(issue *models.Issue) {
 | 
				
			||||||
	models.UpdateIssueIndexer(issue)
 | 
						issue_indexer.UpdateIssueIndexer(issue)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *indexerNotifier) NotifyNewPullRequest(pr *models.PullRequest) {
 | 
					func (r *indexerNotifier) NotifyNewPullRequest(pr *models.PullRequest) {
 | 
				
			||||||
	models.UpdateIssueIndexer(pr.Issue)
 | 
						issue_indexer.UpdateIssueIndexer(pr.Issue)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *indexerNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
 | 
					func (r *indexerNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
 | 
				
			||||||
@@ -67,7 +68,7 @@ func (r *indexerNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		models.UpdateIssueIndexer(c.Issue)
 | 
							issue_indexer.UpdateIssueIndexer(c.Issue)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -91,18 +92,18 @@ func (r *indexerNotifier) NotifyDeleteComment(doer *models.User, comment *models
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// reload comments to delete the old comment
 | 
							// reload comments to delete the old comment
 | 
				
			||||||
		models.UpdateIssueIndexer(comment.Issue)
 | 
							issue_indexer.UpdateIssueIndexer(comment.Issue)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *indexerNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
 | 
					func (r *indexerNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
 | 
				
			||||||
	models.DeleteRepoIssueIndexer(repo)
 | 
						issue_indexer.DeleteRepoIssueIndexer(repo)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *indexerNotifier) NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) {
 | 
					func (r *indexerNotifier) NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) {
 | 
				
			||||||
	models.UpdateIssueIndexer(issue)
 | 
						issue_indexer.UpdateIssueIndexer(issue)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
 | 
					func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
 | 
				
			||||||
	models.UpdateIssueIndexer(issue)
 | 
						issue_indexer.UpdateIssueIndexer(issue)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/notification"
 | 
						"code.gitea.io/gitea/modules/notification"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -77,7 +78,7 @@ func ListIssues(ctx *context.APIContext) {
 | 
				
			|||||||
	var labelIDs []int64
 | 
						var labelIDs []int64
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if len(keyword) > 0 {
 | 
						if len(keyword) > 0 {
 | 
				
			||||||
		issueIDs, err = models.SearchIssuesByKeyword(ctx.Repo.Repository.ID, keyword)
 | 
							issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx.Repo.Repository.ID, keyword)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if splitted := strings.Split(ctx.Query("labels"), ","); len(splitted) > 0 {
 | 
						if splitted := strings.Split(ctx.Query("labels"), ","); len(splitted) > 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/cache"
 | 
						"code.gitea.io/gitea/modules/cache"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/cron"
 | 
						"code.gitea.io/gitea/modules/cron"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/highlight"
 | 
						"code.gitea.io/gitea/modules/highlight"
 | 
				
			||||||
 | 
						issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/mailer"
 | 
						"code.gitea.io/gitea/modules/mailer"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
@@ -90,7 +91,7 @@ func GlobalInit() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Booting long running goroutines.
 | 
							// Booting long running goroutines.
 | 
				
			||||||
		cron.NewContext()
 | 
							cron.NewContext()
 | 
				
			||||||
		if err := models.InitIssueIndexer(); err != nil {
 | 
							if err := issue_indexer.InitIssueIndexer(false); err != nil {
 | 
				
			||||||
			log.Fatal(4, "Failed to initialize issue indexer: %v", err)
 | 
								log.Fatal(4, "Failed to initialize issue indexer: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		models.InitRepoIndexer()
 | 
							models.InitRepoIndexer()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/auth"
 | 
						"code.gitea.io/gitea/modules/auth"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup/markdown"
 | 
						"code.gitea.io/gitea/modules/markup/markdown"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/notification"
 | 
						"code.gitea.io/gitea/modules/notification"
 | 
				
			||||||
@@ -146,7 +147,7 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var issueIDs []int64
 | 
						var issueIDs []int64
 | 
				
			||||||
	if len(keyword) > 0 {
 | 
						if len(keyword) > 0 {
 | 
				
			||||||
		issueIDs, err = models.SearchIssuesByKeyword(repo.ID, keyword)
 | 
							issueIDs, err = issue_indexer.SearchIssuesByKeyword(repo.ID, keyword)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("issueIndexer.Search", err)
 | 
								ctx.ServerError("issueIndexer.Search", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user