mirror of
https://github.com/navidrome/navidrome.git
synced 2026-11-06 02:08:47 +01:00
Merge 7bb2c0968f into 52135913d4
This commit is contained in:
commit
2eb24ca3ce
3 changed files with 63 additions and 0 deletions
|
|
@ -72,6 +72,10 @@ func legacySearchExpr(tableName string, s string) Sqlizer {
|
|||
// likeSearchColumns defines the core columns to search with LIKE queries.
|
||||
// These are the primary user-visible fields for each entity type.
|
||||
// Used as a fallback when FTS5 cannot handle the query (e.g., CJK text, punctuation-only input).
|
||||
//
|
||||
// Each column needs a matching COLLATE NOCASE index (migration
|
||||
// add_like_search_covering_indexes) so these LIKE scans hit a narrow covering index
|
||||
// instead of the full table. The "likeSearchColumns covering indexes" test enforces this.
|
||||
var likeSearchColumns = map[string][]string{
|
||||
"media_file": {"title", "album", "artist", "album_artist"},
|
||||
"album": {"name", "album_artist"},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package persistence
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/conf/configtest"
|
||||
|
|
@ -132,3 +134,37 @@ var _ = Describe("Legacy Integration Search", func() {
|
|||
Expect(results).ToNot(BeEmpty(), "Max=0 should mean no limit, not LIMIT 0")
|
||||
})
|
||||
})
|
||||
|
||||
// Guards the invariant that every column searched by the LIKE fallback has a COLLATE NOCASE
|
||||
// covering index (migration add_like_search_covering_indexes). Without it a CJK/punctuation
|
||||
// search does a full table scan. If you add a column to likeSearchColumns without an index,
|
||||
// this test fails — keep the two in sync.
|
||||
var _ = Describe("likeSearchColumns covering indexes", func() {
|
||||
It("has a COLLATE NOCASE index for every searched column", func() {
|
||||
var indexSQLs []string
|
||||
err := GetDBXBuilder().
|
||||
NewQuery("SELECT sql FROM sqlite_master WHERE type='index' AND sql IS NOT NULL").
|
||||
Column(&indexSQLs)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for table, columns := range likeSearchColumns {
|
||||
// Match the index's target table precisely: "ON <table> (" (case-insensitive).
|
||||
tableRe := regexp.MustCompile(`(?i)\bon\s+` + regexp.QuoteMeta(table) + `\s*\(`)
|
||||
for _, col := range columns {
|
||||
// Match e.g. "(title COLLATE NOCASE)" — column immediately followed by the
|
||||
// NOCASE collation, case-insensitive (SQLite emits both "COLLATE" and "collate").
|
||||
colRe := regexp.MustCompile(`(?i)\b` + regexp.QuoteMeta(col) + `\s+collate\s+nocase\b`)
|
||||
found := false
|
||||
for _, sql := range indexSQLs {
|
||||
if tableRe.MatchString(sql) && colRe.MatchString(sql) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(found).To(BeTrue(),
|
||||
fmt.Sprintf("missing COLLATE NOCASE index for %s.%s — add it to the "+
|
||||
"add_like_search_covering_indexes migration to keep LIKE search fast", table, col))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue