aboutsummaryrefslogtreecommitdiffstats
path: root/cache
diff options
context:
space:
mode:
authorVincent Tiu <46623413+Invincibot@users.noreply.github.com>2020-11-17 21:35:31 +0800
committerGitHub <noreply@github.com>2020-11-17 21:35:31 +0800
commit466b5183c371b4ac7f905e33c4f8f36ddb7db9ff (patch)
tree1b477423060292af296b32971cda1ca0045e0c0d /cache
parent902997f53771babb2f9ea1bb6288c2ec295c4c9e (diff)
parentee3841155e5c58b85408f29a8d44630da5de63f7 (diff)
downloadgit-bug-466b5183c371b4ac7f905e33c4f8f36ddb7db9ff.tar.gz
Merge pull request #446 from MichaelMure/bug-fts
bug full text search
Diffstat (limited to 'cache')
-rw-r--r--cache/repo_cache.go19
-rw-r--r--cache/repo_cache_bug.go98
2 files changed, 115 insertions, 2 deletions
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index 4fc88015..8bce3d8c 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -15,6 +15,7 @@ import (
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/process"
+ "github.com/blevesearch/bleve"
)
// 1: original format
@@ -56,6 +57,8 @@ type RepoCache struct {
muBug sync.RWMutex
// excerpt of bugs data for all bugs
bugExcerpts map[entity.Id]*BugExcerpt
+ // searchable cache of all bugs
+ searchCache bleve.Index
// bug loaded in memory
bugs map[entity.Id]*BugCache
// loadedBugs is an LRU cache that records which bugs the cache has loaded in
@@ -116,6 +119,7 @@ func (c *RepoCache) load() error {
if err != nil {
return err
}
+
return c.loadIdentityCache()
}
@@ -166,6 +170,11 @@ func (c *RepoCache) Close() error {
c.bugs = make(map[entity.Id]*BugCache)
c.bugExcerpts = nil
+ if c.searchCache != nil {
+ c.searchCache.Close()
+ c.searchCache = nil
+ }
+
lockPath := repoLockFilePath(c.repo)
return os.Remove(lockPath)
}
@@ -198,6 +207,11 @@ func (c *RepoCache) buildCache() error {
allBugs := bug.ReadAllLocal(c.repo)
+ err := c.createBleveIndex()
+ if err != nil {
+ return fmt.Errorf("Unable to create search cache. Error: %v", err)
+ }
+
for b := range allBugs {
if b.Err != nil {
return b.Err
@@ -205,9 +219,14 @@ func (c *RepoCache) buildCache() error {
snap := b.Bug.Compile()
c.bugExcerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, &snap)
+
+ if err := c.addBugToSearchIndex(&snap); err != nil {
+ return err
+ }
}
_, _ = fmt.Fprintln(os.Stderr, "Done.")
+
return nil
}
diff --git a/cache/repo_cache_bug.go b/cache/repo_cache_bug.go
index 0c583e0d..d57e1bce 100644
--- a/cache/repo_cache_bug.go
+++ b/cache/repo_cache_bug.go
@@ -8,15 +8,20 @@ import (
"os"
"path"
"sort"
+ "strings"
"time"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/query"
"github.com/MichaelMure/git-bug/repository"
+ "github.com/blevesearch/bleve"
)
-const bugCacheFile = "bug-cache"
+const (
+ bugCacheFile = "bug-cache"
+ searchCacheDir = "search-cache"
+)
var errBugNotInCache = errors.New("bug missing from cache")
@@ -24,6 +29,10 @@ func bugCacheFilePath(repo repository.Repo) string {
return path.Join(repo.GetPath(), "git-bug", bugCacheFile)
}
+func searchCacheDirPath(repo repository.Repo) string {
+ return path.Join(repo.GetPath(), "git-bug", searchCacheDir)
+}
+
// bugUpdated is a callback to trigger when the excerpt of a bug changed,
// that is each time a bug is updated
func (c *RepoCache) bugUpdated(id entity.Id) error {
@@ -43,6 +52,10 @@ func (c *RepoCache) bugUpdated(id entity.Id) error {
c.bugExcerpts[id] = NewBugExcerpt(b.bug, b.Snapshot())
c.muBug.Unlock()
+ if err := c.addBugToSearchIndex(b.Snapshot()); err != nil {
+ return err
+ }
+
// we only need to write the bug cache
return c.writeBugCache()
}
@@ -74,6 +87,42 @@ func (c *RepoCache) loadBugCache() error {
}
c.bugExcerpts = aux.Excerpts
+
+ blevePath := searchCacheDirPath(c.repo)
+ searchCache, err := bleve.Open(blevePath)
+ if err != nil {
+ return fmt.Errorf("Unable to open search cache. Error: %v", err)
+ }
+ c.searchCache = searchCache
+
+ count, err := c.searchCache.DocCount()
+ if err != nil {
+ return err
+ }
+ if count != uint64(len(c.bugExcerpts)) {
+ return fmt.Errorf("count mismatch between bleve and bug excerpts")
+ }
+
+ return nil
+}
+
+func (c *RepoCache) createBleveIndex() error {
+ blevePath := searchCacheDirPath(c.repo)
+
+ _ = os.RemoveAll(blevePath)
+
+ mapping := bleve.NewIndexMapping()
+ mapping.DefaultAnalyzer = "en"
+
+ dir := searchCacheDirPath(c.repo)
+
+ bleveIndex, err := bleve.New(dir, mapping)
+ if err != nil {
+ return err
+ }
+
+ c.searchCache = bleveIndex
+
return nil
}
@@ -255,8 +304,34 @@ func (c *RepoCache) QueryBugs(q *query.Query) []entity.Id {
matcher := compileMatcher(q.Filters)
var filtered []*BugExcerpt
+ var foundBySearch map[entity.Id]*BugExcerpt
- for _, excerpt := range c.bugExcerpts {
+ if q.Search != nil {
+ foundBySearch = map[entity.Id]*BugExcerpt{}
+
+ terms := make([]string, len(q.Search))
+ copy(terms, q.Search)
+ for i, search := range q.Search {
+ if strings.Contains(search, " ") {
+ terms[i] = fmt.Sprintf("\"%s\"", search)
+ }
+ }
+
+ bleveQuery := bleve.NewQueryStringQuery(strings.Join(terms, " "))
+ bleveSearch := bleve.NewSearchRequest(bleveQuery)
+ searchResults, err := c.searchCache.Search(bleveSearch)
+ if err != nil {
+ panic("bleve search failed")
+ }
+
+ for _, hit := range searchResults.Hits {
+ foundBySearch[entity.Id(hit.ID)] = c.bugExcerpts[entity.Id(hit.ID)]
+ }
+ } else {
+ foundBySearch = c.bugExcerpts
+ }
+
+ for _, excerpt := range foundBySearch {
if matcher.Match(excerpt, c) {
filtered = append(filtered, excerpt)
}
@@ -423,3 +498,22 @@ func (c *RepoCache) RemoveBug(prefix string) error {
return c.writeBugCache()
}
+
+func (c *RepoCache) addBugToSearchIndex(snap *bug.Snapshot) error {
+ searchableBug := struct {
+ Text []string
+ }{}
+
+ for _, comment := range snap.Comments {
+ searchableBug.Text = append(searchableBug.Text, comment.Message)
+ }
+
+ searchableBug.Text = append(searchableBug.Text, snap.Title)
+
+ err := c.searchCache.Index(snap.Id().String(), searchableBug)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}