Skip to content
This repository was archived by the owner on Nov 25, 2024. It is now read-only.

Commit e8b2162

Browse files
authored
Add /search tests (#3025)
1 parent aa1bda4 commit e8b2162

File tree

5 files changed

+389
-50
lines changed

5 files changed

+389
-50
lines changed

internal/fulltext/bleve.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package fulltext
1919

2020
import (
21+
"regexp"
2122
"strings"
2223

2324
"github.com/blevesearch/bleve/v2"
@@ -60,6 +61,7 @@ type Indexer interface {
6061
Index(elements ...IndexElement) error
6162
Delete(eventID string) error
6263
Search(term string, roomIDs, keys []string, limit, from int, orderByStreamPos bool) (*bleve.SearchResult, error)
64+
GetHighlights(result *bleve.SearchResult) []string
6365
Close() error
6466
}
6567

@@ -124,6 +126,47 @@ func (f *Search) Delete(eventID string) error {
124126
return f.FulltextIndex.Delete(eventID)
125127
}
126128

129+
var highlightMatcher = regexp.MustCompile("<mark>(.*?)</mark>")
130+
131+
// GetHighlights extracts the highlights from a SearchResult.
132+
func (f *Search) GetHighlights(result *bleve.SearchResult) []string {
133+
if result == nil {
134+
return []string{}
135+
}
136+
137+
seenMatches := make(map[string]struct{})
138+
139+
for _, hit := range result.Hits {
140+
if hit.Fragments == nil {
141+
continue
142+
}
143+
fragments, ok := hit.Fragments["Content"]
144+
if !ok {
145+
continue
146+
}
147+
for _, x := range fragments {
148+
substringMatches := highlightMatcher.FindAllStringSubmatch(x, -1)
149+
for _, matches := range substringMatches {
150+
for i := range matches {
151+
if i == 0 { // skip first match, this is the complete substring match
152+
continue
153+
}
154+
if _, ok := seenMatches[matches[i]]; ok {
155+
continue
156+
}
157+
seenMatches[matches[i]] = struct{}{}
158+
}
159+
}
160+
}
161+
}
162+
163+
res := make([]string, 0, len(seenMatches))
164+
for m := range seenMatches {
165+
res = append(res, m)
166+
}
167+
return res
168+
}
169+
127170
// Search searches the index given a search term, roomIDs and keys.
128171
func (f *Search) Search(term string, roomIDs, keys []string, limit, from int, orderByStreamPos bool) (*bleve.SearchResult, error) {
129172
qry := bleve.NewConjunctionQuery()
@@ -163,6 +206,10 @@ func (f *Search) Search(term string, roomIDs, keys []string, limit, from int, or
163206
s.SortBy([]string{"-StreamPosition"})
164207
}
165208

209+
// Highlight some words
210+
s.Highlight = bleve.NewHighlight()
211+
s.Highlight.Fields = []string{"Content"}
212+
166213
return f.FulltextIndex.Search(s)
167214
}
168215

internal/fulltext/bleve_test.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,50 +160,56 @@ func TestSearch(t *testing.T) {
160160
roomIndex []int
161161
}
162162
tests := []struct {
163-
name string
164-
args args
165-
wantCount int
166-
wantErr bool
163+
name string
164+
args args
165+
wantCount int
166+
wantErr bool
167+
wantHighlights []string
167168
}{
168169
{
169-
name: "Can search for many results in one room",
170-
wantCount: 16,
170+
name: "Can search for many results in one room",
171+
wantCount: 16,
172+
wantHighlights: []string{"lorem"},
171173
args: args{
172174
term: "lorem",
173175
roomIndex: []int{0},
174176
limit: 20,
175177
},
176178
},
177179
{
178-
name: "Can search for one result in one room",
179-
wantCount: 1,
180+
name: "Can search for one result in one room",
181+
wantCount: 1,
182+
wantHighlights: []string{"lorem"},
180183
args: args{
181184
term: "lorem",
182185
roomIndex: []int{16},
183186
limit: 20,
184187
},
185188
},
186189
{
187-
name: "Can search for many results in multiple rooms",
188-
wantCount: 17,
190+
name: "Can search for many results in multiple rooms",
191+
wantCount: 17,
192+
wantHighlights: []string{"lorem"},
189193
args: args{
190194
term: "lorem",
191195
roomIndex: []int{0, 16},
192196
limit: 20,
193197
},
194198
},
195199
{
196-
name: "Can search for many results in all rooms, reversed",
197-
wantCount: 30,
200+
name: "Can search for many results in all rooms, reversed",
201+
wantCount: 30,
202+
wantHighlights: []string{"lorem"},
198203
args: args{
199204
term: "lorem",
200205
limit: 30,
201206
orderByStreamPos: true,
202207
},
203208
},
204209
{
205-
name: "Can search for specific search room name",
206-
wantCount: 1,
210+
name: "Can search for specific search room name",
211+
wantCount: 1,
212+
wantHighlights: []string{"testing"},
207213
args: args{
208214
term: "testing",
209215
roomIndex: []int{},
@@ -212,8 +218,9 @@ func TestSearch(t *testing.T) {
212218
},
213219
},
214220
{
215-
name: "Can search for specific search room topic",
216-
wantCount: 1,
221+
name: "Can search for specific search room topic",
222+
wantCount: 1,
223+
wantHighlights: []string{"fulltext"},
217224
args: args{
218225
term: "fulltext",
219226
roomIndex: []int{},
@@ -222,6 +229,7 @@ func TestSearch(t *testing.T) {
222229
},
223230
},
224231
}
232+
225233
for _, tt := range tests {
226234
t.Run(tt.name, func(t *testing.T) {
227235
f, ctx := mustOpenIndex(t, "")
@@ -238,6 +246,12 @@ func TestSearch(t *testing.T) {
238246
t.Errorf("Search() error = %v, wantErr %v", err, tt.wantErr)
239247
return
240248
}
249+
250+
highlights := f.GetHighlights(got)
251+
if !reflect.DeepEqual(highlights, tt.wantHighlights) {
252+
t.Errorf("Search() got highligts = %v, want %v", highlights, tt.wantHighlights)
253+
}
254+
241255
if !reflect.DeepEqual(len(got.Hits), tt.wantCount) {
242256
t.Errorf("Search() got = %v, want %v", len(got.Hits), tt.wantCount)
243257
}

internal/fulltext/bleve_wasm.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Indexer interface {
3333
Index(elements ...IndexElement) error
3434
Delete(eventID string) error
3535
Search(term string, roomIDs, keys []string, limit, from int, orderByStreamPos bool) (SearchResult, error)
36+
GetHighlights(result SearchResult) []string
3637
Close() error
3738
}
3839

@@ -71,3 +72,7 @@ func (f *Search) Delete(eventID string) error {
7172
func (f *Search) Search(term string, roomIDs, keys []string, limit, from int, orderByStreamPos bool) (SearchResult, error) {
7273
return SearchResult{}, nil
7374
}
75+
76+
func (f *Search) GetHighlights(result SearchResult) []string {
77+
return []string{}
78+
}

syncapi/routing/search.go

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"net/http"
2020
"sort"
2121
"strconv"
22-
"strings"
2322
"time"
2423

2524
"github.com/blevesearch/bleve/v2/search"
@@ -123,8 +122,8 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts
123122
return util.JSONResponse{
124123
Code: http.StatusOK,
125124
JSON: SearchResponse{
126-
SearchCategories: SearchCategories{
127-
RoomEvents: RoomEvents{
125+
SearchCategories: SearchCategoriesResponse{
126+
RoomEvents: RoomEventsResponse{
128127
Count: int(result.Total),
129128
NextBatch: nil,
130129
},
@@ -158,7 +157,7 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts
158157
}
159158

160159
groups := make(map[string]RoomResult)
161-
knownUsersProfiles := make(map[string]ProfileInfo)
160+
knownUsersProfiles := make(map[string]ProfileInfoResponse)
162161

163162
// Sort the events by depth, as the returned values aren't ordered
164163
if orderByTime {
@@ -180,7 +179,7 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts
180179
return jsonerror.InternalServerError()
181180
}
182181

183-
profileInfos := make(map[string]ProfileInfo)
182+
profileInfos := make(map[string]ProfileInfoResponse)
184183
for _, ev := range append(eventsBefore, eventsAfter...) {
185184
profile, ok := knownUsersProfiles[event.Sender()]
186185
if !ok {
@@ -192,7 +191,7 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts
192191
if stateEvent == nil {
193192
continue
194193
}
195-
profile = ProfileInfo{
194+
profile = ProfileInfoResponse{
196195
AvatarURL: gjson.GetBytes(stateEvent.Content(), "avatar_url").Str,
197196
DisplayName: gjson.GetBytes(stateEvent.Content(), "displayname").Str,
198197
}
@@ -237,13 +236,13 @@ func Search(req *http.Request, device *api.Device, syncDB storage.Database, fts
237236
}
238237

239238
res := SearchResponse{
240-
SearchCategories: SearchCategories{
241-
RoomEvents: RoomEvents{
239+
SearchCategories: SearchCategoriesResponse{
240+
RoomEvents: RoomEventsResponse{
242241
Count: int(result.Total),
243242
Groups: Groups{RoomID: groups},
244243
Results: results,
245244
NextBatch: nextBatchResult,
246-
Highlights: strings.Split(searchReq.SearchCategories.RoomEvents.SearchTerm, " "),
245+
Highlights: fts.GetHighlights(result),
247246
State: stateForRooms,
248247
},
249248
},
@@ -286,30 +285,40 @@ func contextEvents(
286285
return eventsBefore, eventsAfter, err
287286
}
288287

288+
type EventContext struct {
289+
AfterLimit int `json:"after_limit,omitempty"`
290+
BeforeLimit int `json:"before_limit,omitempty"`
291+
IncludeProfile bool `json:"include_profile,omitempty"`
292+
}
293+
294+
type GroupBy struct {
295+
Key string `json:"key"`
296+
}
297+
298+
type Groupings struct {
299+
GroupBy []GroupBy `json:"group_by"`
300+
}
301+
302+
type RoomEvents struct {
303+
EventContext EventContext `json:"event_context"`
304+
Filter gomatrixserverlib.RoomEventFilter `json:"filter"`
305+
Groupings Groupings `json:"groupings"`
306+
IncludeState bool `json:"include_state"`
307+
Keys []string `json:"keys"`
308+
OrderBy string `json:"order_by"`
309+
SearchTerm string `json:"search_term"`
310+
}
311+
312+
type SearchCategories struct {
313+
RoomEvents RoomEvents `json:"room_events"`
314+
}
315+
289316
type SearchRequest struct {
290-
SearchCategories struct {
291-
RoomEvents struct {
292-
EventContext struct {
293-
AfterLimit int `json:"after_limit,omitempty"`
294-
BeforeLimit int `json:"before_limit,omitempty"`
295-
IncludeProfile bool `json:"include_profile,omitempty"`
296-
} `json:"event_context"`
297-
Filter gomatrixserverlib.RoomEventFilter `json:"filter"`
298-
Groupings struct {
299-
GroupBy []struct {
300-
Key string `json:"key"`
301-
} `json:"group_by"`
302-
} `json:"groupings"`
303-
IncludeState bool `json:"include_state"`
304-
Keys []string `json:"keys"`
305-
OrderBy string `json:"order_by"`
306-
SearchTerm string `json:"search_term"`
307-
} `json:"room_events"`
308-
} `json:"search_categories"`
317+
SearchCategories SearchCategories `json:"search_categories"`
309318
}
310319

311320
type SearchResponse struct {
312-
SearchCategories SearchCategories `json:"search_categories"`
321+
SearchCategories SearchCategoriesResponse `json:"search_categories"`
313322
}
314323
type RoomResult struct {
315324
NextBatch *string `json:"next_batch,omitempty"`
@@ -332,22 +341,22 @@ type SearchContextResponse struct {
332341
EventsAfter []gomatrixserverlib.ClientEvent `json:"events_after"`
333342
EventsBefore []gomatrixserverlib.ClientEvent `json:"events_before"`
334343
Start string `json:"start"`
335-
ProfileInfo map[string]ProfileInfo `json:"profile_info"`
344+
ProfileInfo map[string]ProfileInfoResponse `json:"profile_info"`
336345
}
337346

338-
type ProfileInfo struct {
347+
type ProfileInfoResponse struct {
339348
AvatarURL string `json:"avatar_url"`
340349
DisplayName string `json:"display_name"`
341350
}
342351

343-
type RoomEvents struct {
352+
type RoomEventsResponse struct {
344353
Count int `json:"count"`
345354
Groups Groups `json:"groups"`
346355
Highlights []string `json:"highlights"`
347356
NextBatch *string `json:"next_batch,omitempty"`
348357
Results []Result `json:"results"`
349358
State map[string][]gomatrixserverlib.ClientEvent `json:"state,omitempty"`
350359
}
351-
type SearchCategories struct {
352-
RoomEvents RoomEvents `json:"room_events"`
360+
type SearchCategoriesResponse struct {
361+
RoomEvents RoomEventsResponse `json:"room_events"`
353362
}

0 commit comments

Comments
 (0)