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
124
|
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
type DocumentMatch struct {
search.DocumentMatch
Data options.NixOption
}
type Result struct {
*bleve.SearchResult
Hits []DocumentMatch
}
type ReadIndex struct {
index bleve.Index
meta *IndexMeta
}
func Open(dataRoot string) (*ReadIndex, error) {
indexPath := path.Join(dataRoot, indexBaseName)
metaPath := path.Join(dataRoot, metaBaseName)
idx, err := bleve.Open(indexPath)
if err != nil {
return nil, errors.WithMessagef(err, "unable to open index at path %s", indexPath)
}
meta, err := openMeta(metaPath)
if err != nil {
return nil, errors.WithMessagef(err, "unable to open metadata at path %s", metaPath)
}
return &ReadIndex{
idx,
meta,
}, 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
}
}
|