Files
mgdigital 6358b27bc6 Search query string rework (#96)
- `search_string` field is removed
- text search vector for `content` and `torrent_contents` is generated in code with improvements in normalisation and tokenisation
- the slow `LIKE` query is removed from text search
- a query DSL is implemented which translates the input search string to a Postgres tsquery
- a CLI command (`reindex`) is provided for updating the tsv for pre-existing records
- the unused `tsv` and `search_string` fields are removed from the `torrents` table

See https://github.com/bitmagnet-io/bitmagnet/issues/89
2024-01-07 11:51:40 +00:00

83 lines
1.7 KiB
Go

package fts
import (
"github.com/mozillazg/go-unidecode/table"
"strings"
"unicode"
)
func Tokenize(str string) [][]string {
l := tokenizerLexer{newLexer(str)}
var tokens [][]string
for {
phrase := l.readPhrase()
if len(phrase) == 0 {
break
}
tokens = append(tokens, phrase)
}
return tokens
}
type tokenizerLexer struct {
lexer
}
func TokenizeFlat(str string) []string {
var tokens []string
for _, phrase := range Tokenize(str) {
tokens = append(tokens, phrase...)
}
return tokens
}
func (l *tokenizerLexer) readPhrase() []string {
var phrase []string
var lexeme string
breakWord := func() {
if lexeme != "" {
phrase = append(phrase, lexeme)
lexeme = ""
}
}
appendStr := func(str string) {
lexeme = lexeme + str
}
for {
if l.isEof() {
breakWord()
return phrase
}
if ch, ok := l.readIf(IsWordChar); ok {
ch = unicode.ToLower(ch)
if ch < unicode.MaxASCII {
appendStr(string(ch))
} else {
// if the character is determined to be a language with unspaced words (e.g. Chinese, Japanese),
// each character will become a token; using this cutoff might not be perfect...
isNonBreakingLang := ch > '\u1FFF'
if isNonBreakingLang {
breakWord()
}
section := ch >> 8 // Chop off the last two hex digits
position := ch % 256 // Last two hex digits
if tb, ok := table.Tables[section]; ok {
if len(tb) > int(position) {
subst := tb[position]
appendStr(strings.TrimSpace(subst))
if isNonBreakingLang || len(subst) == 0 || subst[len(subst)-1] == ' ' {
breakWord()
}
}
}
}
continue
}
breakWord()
if len(phrase) > 0 {
return phrase
}
l.read()
}
}