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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. UpdateRepoIndexer(repo)
  126. }
  127. }
  128. }
  129. // UpdateRepoIndexer add/update all issues of the repositories
  130. func UpdateRepoIndexer(repo *models.Repository) {
  131. is, err := models.Issues(&models.IssuesOptions{
  132. RepoIDs: []int64{repo.ID},
  133. IsClosed: util.OptionalBoolNone,
  134. IsPull: util.OptionalBoolNone,
  135. })
  136. if err != nil {
  137. log.Error("Issues: %v", err)
  138. return
  139. }
  140. if err = models.IssueList(is).LoadDiscussComments(); err != nil {
  141. log.Error("LoadComments: %v", err)
  142. return
  143. }
  144. for _, issue := range is {
  145. UpdateIssueIndexer(issue)
  146. }
  147. }
  148. // UpdateIssueIndexer add/update an issue to the issue indexer
  149. func UpdateIssueIndexer(issue *models.Issue) {
  150. var comments []string
  151. for _, comment := range issue.Comments {
  152. if comment.Type == models.CommentTypeComment {
  153. comments = append(comments, comment.Content)
  154. }
  155. }
  156. _ = issueIndexerQueue.Push(&IndexerData{
  157. ID: issue.ID,
  158. RepoID: issue.RepoID,
  159. Title: issue.Title,
  160. Content: issue.Content,
  161. Comments: comments,
  162. })
  163. }
  164. // DeleteRepoIssueIndexer deletes repo's all issues indexes
  165. func DeleteRepoIssueIndexer(repo *models.Repository) {
  166. var ids []int64
  167. ids, err := models.GetIssueIDsByRepoID(repo.ID)
  168. if err != nil {
  169. log.Error("getIssueIDsByRepoID failed: %v", err)
  170. return
  171. }
  172. if len(ids) == 0 {
  173. return
  174. }
  175. _ = issueIndexerQueue.Push(&IndexerData{
  176. IDs: ids,
  177. IsDelete: true,
  178. })
  179. }
  180. // SearchIssuesByKeyword search issue ids by keywords and repo id
  181. func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) {
  182. var issueIDs []int64
  183. res, err := issueIndexer.Search(keyword, repoID, 1000, 0)
  184. if err != nil {
  185. return nil, err
  186. }
  187. for _, r := range res.Hits {
  188. issueIDs = append(issueIDs, r.ID)
  189. }
  190. return issueIDs, nil
  191. }