package backend import ( "fmt" "sync" "github.com/blevesearch/bleve" ) type ( Index struct { index bleve.Index mu sync.RWMutex } Result struct{ *bleve.SearchResult } ) func (i *Index) isIndexed(filename string, errChan chan<- error) bool { i.mu.RLock() defer i.mu.RUnlock() query := bleve.NewDocIDQuery([]string{filename}) request := bleve.NewSearchRequest(query) result, err := i.index.Search(request) if err != nil { errChan <- fmt.Errorf("error searching for file %v in index: %v", filename, err) return false } return len(result.Hits) > 0 } func (i *Index) indexAllArticles(c *Config, db *DB) error { articles, err := db.GetAllArticles() if err != nil { return fmt.Errorf("error getting all articles from DB: %v", err) } var wg sync.WaitGroup errChan := make(chan error, len(articles)) go func() { wg.Wait() close(errChan) }() for _, article := range articles { wg.Add(1) go func() { defer wg.Done() filename := article.UUID.String() content, err := article.ReadContent(c) if err != nil { errChan <- fmt.Errorf("error reading content of article %v: %v", filename, err) return } if !i.isIndexed(filename, errChan) { if err := i.Add(filename, content); err != nil { errChan <- fmt.Errorf("error adding file %v to index: %v", filename, err) return } } errChan <- nil }() } if err = <-errChan; err != nil { return fmt.Errorf("error(s) indexing all articles: %v", err) } return nil } func InitIndex(c *Config) (*Index, error) { var err error index := new(Index) index.index, err = bleve.Open(c.IndexDir) if err != nil { mapping := bleve.NewIndexMapping() index.index, err = bleve.New(c.IndexDir, mapping) if err != nil { return nil, fmt.Errorf("error creating new index file: %v", err) } } return index, err } func (i *Index) Add(filename, content string) error { i.mu.Lock() defer i.mu.Unlock() doc := map[string]string{ "name": filename, "body": content, } if err := i.index.Index(filename, doc); err != nil { return fmt.Errorf("error indexing file %v: %v", filename, err) } return nil } func (i *Index) Query(q string) (*Result, error) { i.mu.RLock() defer i.mu.RUnlock() query := bleve.NewQueryStringQuery(q) request := bleve.NewSearchRequest(query) result, err := i.index.Search(request) if err != nil { return nil, fmt.Errorf("error querying index for \"%v\": %v", q, err) } if len(result.Hits) < 1 { return nil, nil } return &Result{result}, err } func (i *Index) Delete(filename string) error { i.mu.Lock() defer i.mu.Unlock() if err := i.index.Delete(filename); err != nil { return fmt.Errorf("error deleting file %v from index: %v", filename, err) } return nil }