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

indexer.go 4.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. // issueIndexerUpdateQueue queue of issue ids to be updated
  42. issueIndexerUpdateQueue 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. issueIndexerUpdateQueue = &DummyQueue{}
  66. return nil
  67. }
  68. var err error
  69. switch setting.Indexer.IssueIndexerQueueType {
  70. case setting.LevelQueueType:
  71. issueIndexerUpdateQueue, err = NewLevelQueue(
  72. issueIndexer,
  73. setting.Indexer.IssueIndexerQueueDir,
  74. setting.Indexer.IssueIndexerQueueBatchNumber)
  75. if err != nil {
  76. return err
  77. }
  78. case setting.ChannelQueueType:
  79. issueIndexerUpdateQueue = NewChannelQueue(issueIndexer, setting.Indexer.IssueIndexerQueueBatchNumber)
  80. default:
  81. return fmt.Errorf("Unsupported indexer queue type: %v", setting.Indexer.IssueIndexerQueueType)
  82. }
  83. go issueIndexerUpdateQueue.Run()
  84. if populate {
  85. if syncReindex {
  86. populateIssueIndexer()
  87. } else {
  88. go populateIssueIndexer()
  89. }
  90. }
  91. return nil
  92. }
  93. // populateIssueIndexer populate the issue indexer with issue data
  94. func populateIssueIndexer() {
  95. for page := 1; ; page++ {
  96. repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
  97. Page: page,
  98. PageSize: models.RepositoryListDefaultPageSize,
  99. OrderBy: models.SearchOrderByID,
  100. Private: true,
  101. Collaborate: util.OptionalBoolFalse,
  102. })
  103. if err != nil {
  104. log.Error(4, "SearchRepositoryByName: %v", err)
  105. continue
  106. }
  107. if len(repos) == 0 {
  108. return
  109. }
  110. for _, repo := range repos {
  111. is, err := models.Issues(&models.IssuesOptions{
  112. RepoIDs: []int64{repo.ID},
  113. IsClosed: util.OptionalBoolNone,
  114. IsPull: util.OptionalBoolNone,
  115. })
  116. if err != nil {
  117. log.Error(4, "Issues: %v", err)
  118. continue
  119. }
  120. if err = models.IssueList(is).LoadDiscussComments(); err != nil {
  121. log.Error(4, "LoadComments: %v", err)
  122. continue
  123. }
  124. for _, issue := range is {
  125. UpdateIssueIndexer(issue)
  126. }
  127. }
  128. }
  129. }
  130. // UpdateIssueIndexer add/update an issue to the issue indexer
  131. func UpdateIssueIndexer(issue *models.Issue) {
  132. var comments []string
  133. for _, comment := range issue.Comments {
  134. if comment.Type == models.CommentTypeComment {
  135. comments = append(comments, comment.Content)
  136. }
  137. }
  138. issueIndexerUpdateQueue.Push(&IndexerData{
  139. ID: issue.ID,
  140. RepoID: issue.RepoID,
  141. Title: issue.Title,
  142. Content: issue.Content,
  143. Comments: comments,
  144. })
  145. }
  146. // DeleteRepoIssueIndexer deletes repo's all issues indexes
  147. func DeleteRepoIssueIndexer(repo *models.Repository) {
  148. var ids []int64
  149. ids, err := models.GetIssueIDsByRepoID(repo.ID)
  150. if err != nil {
  151. log.Error(4, "getIssueIDsByRepoID failed: %v", err)
  152. return
  153. }
  154. if len(ids) <= 0 {
  155. return
  156. }
  157. issueIndexerUpdateQueue.Push(&IndexerData{
  158. IDs: ids,
  159. IsDelete: true,
  160. })
  161. }
  162. // SearchIssuesByKeyword search issue ids by keywords and repo id
  163. func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) {
  164. var issueIDs []int64
  165. res, err := issueIndexer.Search(keyword, repoID, 1000, 0)
  166. if err != nil {
  167. return nil, err
  168. }
  169. for _, r := range res.Hits {
  170. issueIDs = append(issueIDs, r.ID)
  171. }
  172. return issueIDs, nil
  173. }