Skip to content

Commit

Permalink
Add result scores and matched words to response (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
bastianjoel authored Aug 22, 2023
1 parent a10e8b8 commit 6ebeccf
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 13 deletions.
6 changes: 3 additions & 3 deletions pkg/search/qserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

type queryItem struct {
q string
fn func([]string, error)
fn func(map[string]Answer, error)
}

// QueryServer manages incoming queries against the database.
Expand Down Expand Up @@ -62,12 +62,12 @@ func (qs *QueryServer) Run(ctx context.Context) {
var errQueryQueueFull = errors.New("query queue full")

// Query searches the database for hits. Returns a list of fqids.
func (qs *QueryServer) Query(q string) (answers []string, err error) {
func (qs *QueryServer) Query(q string) (answers map[string]Answer, err error) {
done := make(chan struct{})
select {
case qs.queries <- queryItem{
q: q,
fn: func(as []string, e error) {
fn: func(as map[string]Answer, e error) {
answers, err = as, e
close(done)
},
Expand Down
25 changes: 22 additions & 3 deletions pkg/search/textindex.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,14 @@ func (ti *TextIndex) build() error {
return nil
}

// Answer contains additional information of an search results answer
type Answer struct {
Score float64
MatchedWords map[string][]string
}

// Search queries the internal index for hits.
func (ti *TextIndex) Search(question string) ([]string, error) {
func (ti *TextIndex) Search(question string) (map[string]Answer, error) {
start := time.Now()
defer func() {
log.Printf("searching for %q took %v\n", question, time.Since(start))
Expand All @@ -291,13 +297,14 @@ func (ti *TextIndex) Search(question string) ([]string, error) {
query.Analyzer = de.AnalyzerName
query.Fuzziness = 1
request := bleve.NewSearchRequest(query)
request.IncludeLocations = true
result, err := ti.index.Search(request)
if err != nil {
return nil, err
}
log.Printf("number hits: %d\n", len(result.Hits))
dupes := map[string]struct{}{}
answers := make([]string, 0, len(result.Hits))
answers := make(map[string]Answer, len(result.Hits))
numDupes := 0

for i := range result.Hits {
Expand All @@ -306,8 +313,20 @@ func (ti *TextIndex) Search(question string) ([]string, error) {
numDupes++
continue
}

matchedWords := map[string][]string{}
for location := range result.Hits[i].Locations {
matchedWords[location] = []string{}
for word := range result.Hits[i].Locations[location] {
matchedWords[location] = append(matchedWords[location], word)
}
}

dupes[fqid] = struct{}{}
answers = append(answers, fqid)
answers[fqid] = Answer{
Score: result.Hits[i].Score,
MatchedWords: matchedWords,
}
}
log.Printf("number of duplicates: %d\n", numDupes)
return answers, nil
Expand Down
29 changes: 22 additions & 7 deletions pkg/web/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ type auRequest struct {
Fields map[string]*meta.CollectionRelation `json:"fields"`
}

func (c *controller) autoupdateRequestFromFQIDs(fqids []string) []auRequest {
func (c *controller) autoupdateRequestFromFQIDs(answers map[string]search.Answer) []auRequest {
collIdxMap := map[string]int{}
var req []auRequest
for _, fqid := range fqids {
for fqid := range answers {
collection, id, found := strings.Cut(fqid, "/")
if !found {
continue
Expand Down Expand Up @@ -125,7 +125,7 @@ func (c *controller) search(w http.ResponseWriter, r *http.Request) {
defer resp.Body.Close()
w.Header().Set("Content-Type", "application/json")

filteredResp, err := transformRestricterResponse(resp.Body)
filteredResp, err := transformRestricterResponse(answers, resp.Body)
if err != nil {
handleErrorWithStatus(w, err)
return
Expand All @@ -147,7 +147,7 @@ func (c *controller) search(w http.ResponseWriter, r *http.Request) {
}

// transforms the autoupdate response to per fqid objects
func transformRestricterResponse(body io.ReadCloser) ([]byte, error) {
func transformRestricterResponse(answers map[string]search.Answer, body io.ReadCloser) ([]byte, error) {
respBody, err := io.ReadAll(body)
if err != nil {
return nil, err
Expand All @@ -158,18 +158,33 @@ func transformRestricterResponse(body io.ReadCloser) ([]byte, error) {
return nil, err
}

transformed := make(map[string]map[string]any)
type resultEntry struct {
Content map[string]any `json:"content"`
MatchedWords map[string][]string `json:"matched_by,omitempty"`
Score *float64 `json:"score,omitempty"`
}
transformed := make(map[string]resultEntry)
for k, v := range restricterResponse {
parts := strings.Split(k, "/")
if len(parts) >= 3 {
fqid := parts[0] + "/" + parts[1]
field := parts[2]

if _, ok := transformed[fqid]; !ok {
transformed[fqid] = make(map[string]any)
var score *float64
var matchedWords map[string][]string
if val, ok := answers[fqid]; ok {
score = &val.Score
matchedWords = val.MatchedWords
}
transformed[fqid] = resultEntry{
Content: make(map[string]any),
MatchedWords: matchedWords,
Score: score,
}
}

transformed[fqid][field] = v
transformed[fqid].Content[field] = v
}
}

Expand Down

0 comments on commit 6ebeccf

Please sign in to comment.