You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

indexer.go 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package issues
  5. import (
  6. "fmt"
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/log"
  9. "code.gitea.io/gitea/modules/setting"
  10. "code.gitea.io/gitea/modules/util"
  11. )
  12. // IndexerData data stored in the issue indexer
  13. type IndexerData struct {
  14. ID int64
  15. RepoID int64
  16. Title string
  17. Content string
  18. Comments []string
  19. IsDelete bool
  20. IDs []int64
  21. }
  22. // Match represents on search result
  23. type Match struct {
  24. ID int64 `json:"id"`
  25. RepoID int64 `json:"repo_id"`
  26. Score float64 `json:"score"`
  27. }
  28. // SearchResult represents search results
  29. type SearchResult struct {
  30. Hits []Match
  31. }
  32. // Indexer defines an inteface to indexer issues contents
  33. type Indexer interface {
  34. Init() (bool, error)
  35. Index(issue []*IndexerData) error
  36. Delete(ids ...int64) error
  37. Search(kw string, repoID int64, limit, start int) (*SearchResult, error)
  38. }
  39. var (
  40. // issueIndexerUpdateQueue queue of issue ids to be updated
  41. issueIndexerUpdateQueue Queue
  42. issueIndexer Indexer
  43. )
  44. // InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
  45. // all issue index done.
  46. func InitIssueIndexer(syncReindex bool) error {
  47. var populate bool
  48. switch setting.Indexer.IssueType {
  49. case "bleve":
  50. issueIndexer = NewBleveIndexer(setting.Indexer.IssuePath)
  51. exist, err := issueIndexer.Init()
  52. if err != nil {
  53. return err
  54. }
  55. populate = !exist
  56. default:
  57. return fmt.Errorf("unknow issue indexer type: %s", setting.Indexer.IssueType)
  58. }
  59. var err error
  60. switch setting.Indexer.IssueIndexerQueueType {
  61. case setting.LevelQueueType:
  62. issueIndexerUpdateQueue, err = NewLevelQueue(
  63. issueIndexer,
  64. setting.Indexer.IssueIndexerQueueDir,
  65. setting.Indexer.IssueIndexerQueueBatchNumber)
  66. if err != nil {
  67. return err
  68. }
  69. case setting.ChannelQueueType:
  70. issueIndexerUpdateQueue = NewChannelQueue(issueIndexer, setting.Indexer.IssueIndexerQueueBatchNumber)
  71. default:
  72. return fmt.Errorf("Unsupported indexer queue type: %v", setting.Indexer.IssueIndexerQueueType)
  73. }
  74. go issueIndexerUpdateQueue.Run()
  75. if populate {
  76. if syncReindex {
  77. populateIssueIndexer()
  78. } else {
  79. go populateIssueIndexer()
  80. }
  81. }
  82. return nil
  83. }
  84. // populateIssueIndexer populate the issue indexer with issue data
  85. func populateIssueIndexer() {
  86. for page := 1; ; page++ {
  87. repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
  88. Page: page,
  89. PageSize: models.RepositoryListDefaultPageSize,
  90. OrderBy: models.SearchOrderByID,
  91. Private: true,
  92. Collaborate: util.OptionalBoolFalse,
  93. })
  94. if err != nil {
  95. log.Error(4, "SearchRepositoryByName: %v", err)
  96. continue
  97. }
  98. if len(repos) == 0 {
  99. return
  100. }
  101. for _, repo := range repos {
  102. is, err := models.Issues(&models.IssuesOptions{
  103. RepoIDs: []int64{repo.ID},
  104. IsClosed: util.OptionalBoolNone,
  105. IsPull: util.OptionalBoolNone,
  106. })
  107. if err != nil {
  108. log.Error(4, "Issues: %v", err)
  109. continue
  110. }
  111. if err = models.IssueList(is).LoadDiscussComments(); err != nil {
  112. log.Error(4, "LoadComments: %v", err)
  113. continue
  114. }
  115. for _, issue := range is {
  116. UpdateIssueIndexer(issue)
  117. }
  118. }
  119. }
  120. }
  121. // UpdateIssueIndexer add/update an issue to the issue indexer
  122. func UpdateIssueIndexer(issue *models.Issue) {
  123. var comments []string
  124. for _, comment := range issue.Comments {
  125. if comment.Type == models.CommentTypeComment {
  126. comments = append(comments, comment.Content)
  127. }
  128. }
  129. issueIndexerUpdateQueue.Push(&IndexerData{
  130. ID: issue.ID,
  131. RepoID: issue.RepoID,
  132. Title: issue.Title,
  133. Content: issue.Content,
  134. Comments: comments,
  135. })
  136. }
  137. // DeleteRepoIssueIndexer deletes repo's all issues indexes
  138. func DeleteRepoIssueIndexer(repo *models.Repository) {
  139. var ids []int64
  140. ids, err := models.GetIssueIDsByRepoID(repo.ID)
  141. if err != nil {
  142. log.Error(4, "getIssueIDsByRepoID failed: %v", err)
  143. return
  144. }
  145. if len(ids) <= 0 {
  146. return
  147. }
  148. issueIndexerUpdateQueue.Push(&IndexerData{
  149. IDs: ids,
  150. IsDelete: true,
  151. })
  152. }
  153. // SearchIssuesByKeyword search issue ids by keywords and repo id
  154. func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) {
  155. var issueIDs []int64
  156. res, err := issueIndexer.Search(keyword, repoID, 1000, 0)
  157. if err != nil {
  158. return nil, err
  159. }
  160. for _, r := range res.Hits {
  161. issueIDs = append(issueIDs, r.ID)
  162. }
  163. return issueIDs, nil
  164. }