From d0eafd9bc5f8e017f8ece5e60c174ee11f821344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radu=20Lucu=C8=9B?= Date: Tue, 24 Sep 2024 12:04:47 +0300 Subject: [PATCH] refactor: update search engine to use slice of items instead of map --- search.go | 45 +++++++++++++++++++++++++++++++++++++++------ utils.go | 2 -- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/search.go b/search.go index d959f38..e74a9a2 100644 --- a/search.go +++ b/search.go @@ -8,13 +8,17 @@ import ( type Engine struct { sync.RWMutex - items map[int64][][]rune tolerance int + items []item +} + +type item struct { + Id int64 + Text [][]rune } func NewEngine() *Engine { engine := &Engine{ - items: make(map[int64][][]rune), tolerance: 1, } return engine @@ -32,14 +36,28 @@ func (e *Engine) SetTolerance(tolerance int) { func (e *Engine) SetItem(id int64, text string) { e.Lock() defer e.Unlock() - e.items[id] = tokenize(text) + index, ok := binarySearch(e.items, id) + if ok { + e.items[index].Text = tokenize(text) + return + } + e.items = append(e.items, item{}) + copy(e.items[index+1:], e.items[index:]) + e.items[index] = item{ + Id: id, + Text: tokenize(text), + } } // Remove an item from the search engine. func (e *Engine) DeleteItem(id int64) { e.Lock() defer e.Unlock() - delete(e.items, id) + index, ok := binarySearch(e.items, id) + if !ok { + return + } + e.items = append(e.items[:index], e.items[index+1:]...) } type itemScore struct { @@ -77,13 +95,14 @@ func (e *Engine) Search(opts SearchOptions) SearchResult { e.RLock() defer e.RUnlock() scores := make([]*itemScore, 0) - for id := range e.items { + for i := range e.items { + id := e.items[i].Id if hasIgnore { if _, ok := ignoreMap[id]; ok { continue } } - score := e.score(q, e.items[id]) + score := e.score(q, e.items[i].Text) if score == -1 { continue } @@ -139,3 +158,17 @@ func (e *Engine) score(q, b [][]rune) int { } return score } + +func binarySearch(s []item, target int64) (int, bool) { + n := len(s) + l, r := 0, n + for l < r { + m := int(uint(l+r) >> 1) + if s[m].Id < target { + l = m + 1 + } else { + r = m + } + } + return l, l < n && s[l].Id == target +} diff --git a/utils.go b/utils.go index a213597..c3cdbee 100644 --- a/utils.go +++ b/utils.go @@ -8,8 +8,6 @@ import ( "golang.org/x/text/unicode/norm" ) -type TokenizeFunc func(input string) [][]rune - func tokenize(input string) [][]rune { var tokens [][]rune var token []rune