diff options
author | Ethan Koenig <ethantkoenig@gmail.com> | 2017-09-16 13:16:21 -0700 |
---|---|---|
committer | Lauris BH <lauris@nix.lv> | 2017-09-16 23:16:21 +0300 |
commit | b0f7457d9ef6e16d4a3197f7544035d5d387e201 (patch) | |
tree | dbe6a9b3b74f9a4a6eaa0c0cfe59fa08509edccc /models | |
parent | 52e11b24bf5e395d83ea58c1b0fd6922efe16add (diff) | |
download | gitea-b0f7457d9ef6e16d4a3197f7544035d5d387e201.tar.gz gitea-b0f7457d9ef6e16d4a3197f7544035d5d387e201.zip |
Improve issue search (#2387)
* Improve issue indexer
* Fix new issue sqlite bug
* Different test indexer paths for each db
* Add integration indexer paths to make clean
Diffstat (limited to 'models')
-rw-r--r-- | models/fixtures/issue.yml | 10 | ||||
-rw-r--r-- | models/issue.go | 29 | ||||
-rw-r--r-- | models/issue_comment.go | 24 | ||||
-rw-r--r-- | models/issue_indexer.go | 164 | ||||
-rw-r--r-- | models/pull.go | 2 |
5 files changed, 84 insertions, 145 deletions
diff --git a/models/fixtures/issue.yml b/models/fixtures/issue.yml index b80ada1ba4..2592ad2de0 100644 --- a/models/fixtures/issue.yml +++ b/models/fixtures/issue.yml @@ -5,7 +5,7 @@ poster_id: 1 assignee_id: 1 name: issue1 - content: content1 + content: content for the first issue is_closed: false is_pull: false num_comments: 2 @@ -18,7 +18,7 @@ index: 2 poster_id: 1 name: issue2 - content: content2 + content: content for the second issue milestone_id: 1 is_closed: false is_pull: true @@ -32,7 +32,7 @@ index: 3 poster_id: 1 name: issue3 - content: content4 + content: content for the third issue is_closed: false is_pull: true created_unix: 946684820 @@ -44,7 +44,7 @@ index: 1 poster_id: 2 name: issue4 - content: content4 + content: content for the fourth issue is_closed: true is_pull: false @@ -54,7 +54,7 @@ index: 4 poster_id: 2 name: issue5 - content: content5 + content: content for the fifth issue is_closed: true is_pull: false - diff --git a/models/issue.go b/models/issue.go index 9d081471a7..a8149b123d 100644 --- a/models/issue.go +++ b/models/issue.go @@ -155,6 +155,17 @@ func (issue *Issue) loadPullRequest(e Engine) (err error) { return nil } +func (issue *Issue) loadComments(e Engine) (err error) { + if issue.Comments != nil { + return nil + } + issue.Comments, err = findComments(e, FindCommentsOptions{ + IssueID: issue.ID, + Type: CommentTypeUnknown, + }) + return err +} + func (issue *Issue) loadAttributes(e Engine) (err error) { if err = issue.loadRepo(e); err != nil { return @@ -191,14 +202,8 @@ func (issue *Issue) loadAttributes(e Engine) (err error) { } } - if issue.Comments == nil { - issue.Comments, err = findComments(e, FindCommentsOptions{ - IssueID: issue.ID, - Type: CommentTypeUnknown, - }) - if err != nil { - return fmt.Errorf("getCommentsByIssueID [%d]: %v", issue.ID, err) - } + if err = issue.loadComments(e); err != nil { + return } return nil @@ -577,7 +582,7 @@ func updateIssueCols(e Engine, issue *Issue, cols ...string) error { if _, err := e.Id(issue.ID).Cols(cols...).Update(issue); err != nil { return err } - UpdateIssueIndexer(issue) + UpdateIssueIndexer(issue.ID) return nil } @@ -907,8 +912,6 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { return err } - UpdateIssueIndexer(opts.Issue) - if len(opts.Attachments) > 0 { attachments, err := getAttachmentsByUUIDs(e, opts.Attachments) if err != nil { @@ -947,6 +950,8 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) return fmt.Errorf("Commit: %v", err) } + UpdateIssueIndexer(issue.ID) + if err = NotifyWatchers(&Action{ ActUserID: issue.Poster.ID, ActUser: issue.Poster, @@ -1448,7 +1453,7 @@ func updateIssue(e Engine, issue *Issue) error { if err != nil { return err } - UpdateIssueIndexer(issue) + UpdateIssueIndexer(issue.ID) return nil } diff --git a/models/issue_comment.go b/models/issue_comment.go index 675143437a..084a2a81b1 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -520,7 +520,14 @@ func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) { return nil, err } - return comment, sess.Commit() + if err = sess.Commit(); err != nil { + return nil, err + } + + if opts.Type == CommentTypeComment { + UpdateIssueIndexer(opts.Issue.ID) + } + return comment, nil } // CreateIssueComment creates a plain issue comment. @@ -645,8 +652,12 @@ func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) { // UpdateComment updates information of comment. func UpdateComment(c *Comment) error { - _, err := x.Id(c.ID).AllCols().Update(c) - return err + if _, err := x.Id(c.ID).AllCols().Update(c); err != nil { + return err + } else if c.Type == CommentTypeComment { + UpdateIssueIndexer(c.IssueID) + } + return nil } // DeleteComment deletes the comment @@ -672,5 +683,10 @@ func DeleteComment(comment *Comment) error { return err } - return sess.Commit() + if err := sess.Commit(); err != nil { + return err + } else if comment.Type == CommentTypeComment { + UpdateIssueIndexer(comment.IssueID) + } + return nil } diff --git a/models/issue_indexer.go b/models/issue_indexer.go index 05b324f535..1e14268a0e 100644 --- a/models/issue_indexer.go +++ b/models/issue_indexer.go @@ -6,112 +6,21 @@ package models import ( "fmt" - "os" - "strconv" - "strings" + "code.gitea.io/gitea/modules/indexer" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - - "github.com/blevesearch/bleve" - "github.com/blevesearch/bleve/analysis/analyzer/simple" - "github.com/blevesearch/bleve/search/query" ) -// issueIndexerUpdateQueue queue of issues that need to be updated in the issues -// indexer -var issueIndexerUpdateQueue chan *Issue - -// issueIndexer (thread-safe) index for searching issues -var issueIndexer bleve.Index - -// issueIndexerData data stored in the issue indexer -type issueIndexerData struct { - ID int64 - RepoID int64 - - Title string - Content string -} - -// numericQuery an numeric-equality query for the given value and field -func numericQuery(value int64, field string) *query.NumericRangeQuery { - f := float64(value) - tru := true - q := bleve.NewNumericRangeInclusiveQuery(&f, &f, &tru, &tru) - q.SetField(field) - return q -} - -// SearchIssuesByKeyword searches for issues by given conditions. -// Returns the matching issue IDs -func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) { - terms := strings.Fields(strings.ToLower(keyword)) - indexerQuery := bleve.NewConjunctionQuery( - numericQuery(repoID, "RepoID"), - bleve.NewDisjunctionQuery( - bleve.NewPhraseQuery(terms, "Title"), - bleve.NewPhraseQuery(terms, "Content"), - )) - search := bleve.NewSearchRequestOptions(indexerQuery, 2147483647, 0, false) - search.Fields = []string{"ID"} - - result, err := issueIndexer.Search(search) - if err != nil { - return nil, err - } - - issueIDs := make([]int64, len(result.Hits)) - for i, hit := range result.Hits { - issueIDs[i] = int64(hit.Fields["ID"].(float64)) - } - return issueIDs, nil -} +// issueIndexerUpdateQueue queue of issue ids to be updated +var issueIndexerUpdateQueue chan int64 // InitIssueIndexer initialize issue indexer func InitIssueIndexer() { - _, err := os.Stat(setting.Indexer.IssuePath) - if err != nil { - if os.IsNotExist(err) { - if err = createIssueIndexer(); err != nil { - log.Fatal(4, "CreateIssuesIndexer: %v", err) - } - if err = populateIssueIndexer(); err != nil { - log.Fatal(4, "PopulateIssuesIndex: %v", err) - } - } else { - log.Fatal(4, "InitIssuesIndexer: %v", err) - } - } else { - issueIndexer, err = bleve.Open(setting.Indexer.IssuePath) - if err != nil { - log.Fatal(4, "InitIssuesIndexer, open index: %v", err) - } - } - issueIndexerUpdateQueue = make(chan *Issue, setting.Indexer.UpdateQueueLength) + indexer.InitIssueIndexer(populateIssueIndexer) + issueIndexerUpdateQueue = make(chan int64, setting.Indexer.UpdateQueueLength) go processIssueIndexerUpdateQueue() - // TODO close issueIndexer when Gitea closes -} - -// createIssueIndexer create an issue indexer if one does not already exist -func createIssueIndexer() error { - mapping := bleve.NewIndexMapping() - docMapping := bleve.NewDocumentMapping() - - docMapping.AddFieldMappingsAt("ID", bleve.NewNumericFieldMapping()) - docMapping.AddFieldMappingsAt("RepoID", bleve.NewNumericFieldMapping()) - - textFieldMapping := bleve.NewTextFieldMapping() - textFieldMapping.Analyzer = simple.Name - docMapping.AddFieldMappingsAt("Title", textFieldMapping) - docMapping.AddFieldMappingsAt("Content", textFieldMapping) - - mapping.AddDocumentMapping("issues", docMapping) - - var err error - issueIndexer, err = bleve.New(setting.Indexer.IssuePath, mapping) - return err } // populateIssueIndexer populate the issue indexer with issue data @@ -127,57 +36,64 @@ func populateIssueIndexer() error { if len(repos) == 0 { return nil } - batch := issueIndexer.NewBatch() for _, repo := range repos { issues, err := Issues(&IssuesOptions{ RepoID: repo.ID, IsClosed: util.OptionalBoolNone, IsPull: util.OptionalBoolNone, }) - if err != nil { - return fmt.Errorf("Issues: %v", err) + updates := make([]indexer.IssueIndexerUpdate, len(issues)) + for i, issue := range issues { + updates[i] = issue.update() } - for _, issue := range issues { - err = batch.Index(issue.indexUID(), issue.issueData()) - if err != nil { - return fmt.Errorf("batch.Index: %v", err) - } + if err = indexer.BatchUpdateIssues(updates...); err != nil { + return fmt.Errorf("BatchUpdate: %v", err) } } - if err = issueIndexer.Batch(batch); err != nil { - return fmt.Errorf("index.Batch: %v", err) - } } } func processIssueIndexerUpdateQueue() { for { select { - case issue := <-issueIndexerUpdateQueue: - if err := issueIndexer.Index(issue.indexUID(), issue.issueData()); err != nil { + case issueID := <-issueIndexerUpdateQueue: + issue, err := GetIssueByID(issueID) + if err != nil { + log.Error(4, "issuesIndexer.Index: %v", err) + continue + } + if err = indexer.UpdateIssue(issue.update()); err != nil { log.Error(4, "issuesIndexer.Index: %v", err) } } } } -// indexUID a unique identifier for an issue used in full-text indices -func (issue *Issue) indexUID() string { - return strconv.FormatInt(issue.ID, 36) -} - -func (issue *Issue) issueData() *issueIndexerData { - return &issueIndexerData{ - ID: issue.ID, - RepoID: issue.RepoID, - Title: issue.Title, - Content: issue.Content, +func (issue *Issue) update() indexer.IssueIndexerUpdate { + comments := make([]string, 0, 5) + for _, comment := range issue.Comments { + if comment.Type == CommentTypeComment { + comments = append(comments, comment.Content) + } + } + return indexer.IssueIndexerUpdate{ + IssueID: issue.ID, + Data: &indexer.IssueIndexerData{ + RepoID: issue.RepoID, + Title: issue.Title, + Content: issue.Content, + Comments: comments, + }, } } // UpdateIssueIndexer add/update an issue to the issue indexer -func UpdateIssueIndexer(issue *Issue) { - go func() { - issueIndexerUpdateQueue <- issue - }() +func UpdateIssueIndexer(issueID int64) { + select { + case issueIndexerUpdateQueue <- issueID: + default: + go func() { + issueIndexerUpdateQueue <- issueID + }() + } } diff --git a/models/pull.go b/models/pull.go index 3d2e5c6d97..8754c119f1 100644 --- a/models/pull.go +++ b/models/pull.go @@ -640,6 +640,8 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str return fmt.Errorf("Commit: %v", err) } + UpdateIssueIndexer(pull.ID) + if err = NotifyWatchers(&Action{ ActUserID: pull.Poster.ID, ActUser: pull.Poster, |