internal/search/search.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | package search
import (
"bytes"
"context"
"encoding/gob"
"path"
"searchix/internal/options"
"github.com/blevesearch/bleve/v2"
"github.com/blevesearch/bleve/v2/search"
"github.com/pkg/errors"
)
const ResultsPerPage = 20
const indexFilename = "index.bleve"
type DocumentMatch struct {
search.DocumentMatch
Data options.NixOption
}
type Result struct {
*bleve.SearchResult
Hits []DocumentMatch
}
type ReadIndex struct {
index bleve.Index
}
func Open(dir string) (*ReadIndex, error) {
indexPath := path.Join(dir, indexFilename)
idx, err := bleve.Open(indexPath)
if err != nil {
return nil, errors.WithMessagef(err, "unable to open index at path %s", indexPath)
}
return &ReadIndex{
idx,
}, nil
}
func (index *ReadIndex) GetSource(ctx context.Context, name string) (*bleve.SearchResult, error) {
query := bleve.NewTermQuery(name)
query.SetField("Source")
search := bleve.NewSearchRequest(query)
result, err := index.index.SearchInContext(ctx, search)
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
if err != nil {
return nil, errors.WithMessagef(err, "failed to execute search to find source %s in index", name)
}
}
return result, nil
}
func (index *ReadIndex) Search(ctx context.Context, source string, keyword string, from uint64) (*Result, error) {
sourceQuery := bleve.NewTermQuery(source)
userQuery := bleve.NewMatchQuery(keyword)
userQuery.Analyzer = "option_name"
query := bleve.NewConjunctionQuery(sourceQuery, userQuery)
search := bleve.NewSearchRequest(query)
search.Size = ResultsPerPage
search.Fields = []string{"_data"}
search.Explain = true
if from != 0 {
search.From = int(from)
}
bleveResult, err := index.index.SearchInContext(ctx, search)
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
if err != nil {
return nil, errors.WithMessage(err, "failed to execute search query")
}
results := make([]DocumentMatch, min(ResultsPerPage, bleveResult.Total))
var buf bytes.Buffer
for i, result := range bleveResult.Hits {
_, err = buf.WriteString(result.Fields["_data"].(string))
if err != nil {
return nil, errors.WithMessage(err, "error fetching result data")
}
err = gob.NewDecoder(&buf).Decode(&results[i].Data)
if err != nil {
return nil, errors.WithMessagef(err, "error decoding gob data: %s", buf.String())
}
buf.Reset()
}
return &Result{
SearchResult: bleveResult,
Hits: results,
}, nil
}
}
|