feat(search): нечёткий поиск по сообщениям — по началу слова и по подстроке
Поиск требовал ввести слово целиком. Теперь: - FTS5 ищет по началу слова: «пр» находит «Привет» (последнее слово запроса получает префикс), регистр не важен; - добавлена нормализованная копия текста (`messages.content_lower`, миграция 00007): подстрочные совпадения ищутся через LIKE без учёта регистра, включая кириллицу (SQLite lower() её не приводит — регистр опускает приложение); - при старте индекс дополняется у старых сообщений (порциями по 500); - результаты FTS и LIKE объединяются без дублей, сортировка по новизне; - тест: «пр» и «ивет» находят все три сообщения с «Привет/приветствие/ПРИВЕТ», посторонний текст не попадает.
This commit is contained in:
@@ -240,7 +240,7 @@ func (s *Server) registerMessageRoutes(api huma.API) {
|
||||
if allowed, retryAfter := s.searchLimiter.Allow("search:" + formatSnowflake(user.ID)); !allowed {
|
||||
return nil, rateLimitedError(retryAfter)
|
||||
}
|
||||
messages, err := s.store.SearchMessages(ctx, []uint64{channelID}, toFTSQuery(input.Query), input.Limit)
|
||||
messages, err := s.store.SearchMessages(ctx, []uint64{channelID}, input.Query, input.Limit)
|
||||
if err != nil {
|
||||
return nil, humaError(err)
|
||||
}
|
||||
@@ -856,26 +856,3 @@ func rateLimitedError(retryAfter time.Duration) huma.StatusError {
|
||||
"retry_after_ms": milliseconds,
|
||||
})
|
||||
}
|
||||
|
||||
// toFTSQuery превращает пользовательский ввод в безопасный запрос FTS5:
|
||||
// кавычки и служебные символы экранируются, слова соединяются по AND.
|
||||
func toFTSQuery(input string) string {
|
||||
fields := strings.FieldsFunc(input, func(r rune) bool {
|
||||
return r == ' ' || r == '\t' || r == '\n'
|
||||
})
|
||||
terms := make([]string, 0, len(fields))
|
||||
for _, field := range fields {
|
||||
cleaned := strings.Map(func(r rune) rune {
|
||||
switch r {
|
||||
case '"', '\'', '*', '(', ')', ':', '^', '-', '+':
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, field)
|
||||
if cleaned == "" {
|
||||
continue
|
||||
}
|
||||
terms = append(terms, `"`+cleaned+`"`)
|
||||
}
|
||||
return strings.Join(terms, " AND ")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user