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.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. Total int64
  31. Hits []Match
  32. }
  33. // Indexer defines an inteface to indexer issues contents
  34. type Indexer interface {
  35. Init() (bool, error)
  36. Index(issue []*IndexerData) error
  37. Delete(ids ...int64) error
  38. Search(kw string, repoID int64, limit, start int) (*SearchResult, error)
  39. }
  40. var (
  41. // issueIndexerQueue queue of issue ids to be updated
  42. issueIndexerQueue Queue
  43. issueIndexer Indexer
  44. )
  45. // InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
  46. // all issue index done.
  47. func InitIssueIndexer(syncReindex bool) error {
  48. var populate bool
  49. var dummyQueue bool
  50. switch setting.Indexer.IssueType {
  51. case "bleve":
  52. issueIndexer = NewBleveIndexer(setting.Indexer.IssuePath)
  53. exist, err := issueIndexer.Init()
  54. if err != nil {
  55. return err
  56. }
  57. populate = !exist
  58. case "db":
  59. issueIndexer = &DBIndexer{}
  60. dummyQueue = true
  61. default:
  62. return fmt.Errorf("unknow issue indexer type: %s", setting.Indexer.IssueType)
  63. }
  64. if dummyQueue {
  65. issueIndexerQueue = &DummyQueue{}
  66. return nil
  67. }
  68. var err error
  69. switch setting.Indexer.IssueQueueType {
  70. case setting.LevelQueueType:
  71. issueIndexerQueue, err = NewLevelQueue(
  72. issueIndexer,
  73. setting.Indexer.IssueQueueDir,
  74. setting.Indexer.IssueQueueBatchNumber)
  75. if err != nil {
  76. return err
  77. }
  78. case setting.ChannelQueueType:
  79. issueIndexerQueue = NewChannelQueue(issueIndexer, setting.Indexer.IssueQueueBatchNumber)
  80. case setting.RedisQueueType:
  81. addrs, pass, idx, err := parseConnStr(setting.Indexer.IssueQueueConnStr)
  82. if err != nil {
  83. return err
  84. }
  85. issueIndexerQueue, err = NewRedisQueue(addrs, pass, idx, issueIndexer, setting.Indexer.IssueQueueBatchNumber)
  86. if err != nil {
  87. return err
  88. }
  89. default:
  90. return fmt.Errorf("Unsupported indexer queue type: %v", setting.Indexer.IssueQueueType)
  91. }
  92. go func() {
  93. err = issueIndexerQueue.Run()
  94. if err != nil {
  95. log.Error("issueIndexerQueue.Run: %v", err)
  96. }
  97. }()
  98. if populate {
  99. if syncReindex {
  100. populateIssueIndexer()
  101. } else {
  102. go populateIssueIndexer()
  103. }
  104. }
  105. return nil
  106. }
  107. // populateIssueIndexer populate the issue indexer with issue data
  108. func populateIssueIndexer() {
  109. for page := 1; ; page++ {
  110. repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
  111. Page: page,
  112. PageSize: models.RepositoryListDefaultPageSize,
  113. OrderBy: models.SearchOrderByID,
  114. Private: true,
  115. Collaborate: util.OptionalBoolFalse,
  116. })
  117. if err != nil {
  118. log.Error("SearchRepositoryByName: %v", err)
  119. continue
  120. }
  121. if len(repos) == 0 {
  122. return
  123. }
  124. for _, repo := range repos {
  125. is, err := models.Issues(&models.IssuesOptions{
  126. RepoIDs: []int64{repo.ID},
  127. IsClosed: util.OptionalBoolNone,
  128. IsPull: util.OptionalBoolNone,
  129. })
  130. if err != nil {
  131. log.Error("Issues: %v", err)
  132. continue
  133. }
  134. if err = models.IssueList(is).LoadDiscussComments(); err != nil {
  135. log.Error("LoadComments: %v", err)
  136. continue
  137. }
  138. for _, issue := range is {
  139. UpdateIssueIndexer(issue)
  140. }
  141. }
  142. }
  143. }
  144. // UpdateIssueIndexer add/update an issue to the issue indexer
  145. func UpdateIssueIndexer(issue *models.Issue) {
  146. var comments []string
  147. for _, comment := range issue.Comments {
  148. if comment.Type == models.CommentTypeComment {
  149. comments = append(comments, comment.Content)
  150. }
  151. }
  152. _ = issueIndexerQueue.Push(&IndexerData{
  153. ID: issue.ID,
  154. RepoID: issue.RepoID,
  155. Title: issue.Title,
  156. Content: issue.Content,
  157. Comments: comments,
  158. })
  159. }
  160. // DeleteRepoIssueIndexer deletes repo's all issues indexes
  161. func DeleteRepoIssueIndexer(repo *models.Repository) {
  162. var ids []int64
  163. ids, err := models.GetIssueIDsByRepoID(repo.ID)
  164. if err != nil {
  165. log.Error("getIssueIDsByRepoID failed: %v", err)
  166. return
  167. }
  168. if len(ids) == 0 {
  169. return
  170. }
  171. _ = issueIndexerQueue.Push(&IndexerData{
  172. IDs: ids,
  173. IsDelete: true,
  174. })
  175. }
  176. // SearchIssuesByKeyword search issue ids by keywords and repo id
  177. func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) {
  178. var issueIDs []int64
  179. res, err := issueIndexer.Search(keyword, repoID, 1000, 0)
  180. if err != nil {
  181. return nil, err
  182. }
  183. for _, r := range res.Hits {
  184. issueIDs = append(issueIDs, r.ID)
  185. }
  186. return issueIDs, nil
  187. }