internal/index/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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | package index
import (
"bytes"
"context"
"encoding/gob"
"searchix/internal/config"
"searchix/internal/nix"
"github.com/blevesearch/bleve/v2"
"github.com/blevesearch/bleve/v2/search"
"github.com/pkg/errors"
)
const ResultsPerPage = 20
type DocumentMatch struct {
search.DocumentMatch
Data nix.Importable
}
type Result struct {
*bleve.SearchResult
Hits []DocumentMatch
}
type ReadIndex struct {
index bleve.Index
meta *Meta
}
func (index *ReadIndex) GetEnabledSources() ([]string, error) {
facet := bleve.NewFacetRequest("Source", 100)
query := bleve.NewMatchAllQuery()
search := bleve.NewSearchRequest(query)
search.AddFacet("Source", facet)
results, err := index.index.Search(search)
if err != nil {
return nil, errors.WithMessage(err, "could not get list of enabled sources from index")
}
enabledSources := make([]string, results.Facets["Source"].Terms.Len())
for i, term := range results.Facets["Source"].Terms.Terms() {
enabledSources[i] = term.Term
}
return enabledSources, 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 *config.Source,
keyword string,
from uint64,
) (*Result, error) {
sourceQuery := bleve.NewTermQuery(source.Key)
sourceQuery.SetField("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"}
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
}
}
|