fix(subsonic): handle empty quoted phrases in FTS5 query and search expression
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
@@ -134,7 +134,7 @@ func isDottedAbbreviation(w string, subTokens []string) bool {
|
|||||||
// special characters to prevent query injection.
|
// special characters to prevent query injection.
|
||||||
func buildFTS5Query(userInput string) string {
|
func buildFTS5Query(userInput string) string {
|
||||||
q := strings.TrimSpace(userInput)
|
q := strings.TrimSpace(userInput)
|
||||||
if q == "" {
|
if q == "" || q == `""` {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ var ftsSearchColumns = map[string]string{
|
|||||||
func ftsSearchExpr(tableName string, s string) Sqlizer {
|
func ftsSearchExpr(tableName string, s string) Sqlizer {
|
||||||
q := buildFTS5Query(s)
|
q := buildFTS5Query(s)
|
||||||
if q == "" {
|
if q == "" {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(strings.ReplaceAll(s, `"`, ""))
|
||||||
if s != "" {
|
if s != "" {
|
||||||
log.Trace("Search using LIKE fallback for non-tokenizable query", "table", tableName, "query", s)
|
log.Trace("Search using LIKE fallback for non-tokenizable query", "table", tableName, "query", s)
|
||||||
return likeSearchExpr(tableName, s)
|
return likeSearchExpr(tableName, s)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ var _ = DescribeTable("buildFTS5Query",
|
|||||||
Entry("preserves quoted abbreviation verbatim", `"R.E.M."`, `"R.E.M."`),
|
Entry("preserves quoted abbreviation verbatim", `"R.E.M."`, `"R.E.M."`),
|
||||||
Entry("returns empty string for punctuation-only input", "!!!!!!!", ""),
|
Entry("returns empty string for punctuation-only input", "!!!!!!!", ""),
|
||||||
Entry("returns empty string for mixed punctuation", "!@#$%^&", ""),
|
Entry("returns empty string for mixed punctuation", "!@#$%^&", ""),
|
||||||
|
Entry("returns empty string for empty quoted phrase", `""`, ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = DescribeTable("normalizeForFTS",
|
var _ = DescribeTable("normalizeForFTS",
|
||||||
@@ -204,6 +205,10 @@ var _ = Describe("ftsSearchExpr", func() {
|
|||||||
Expect(ftsSearchExpr("media_file", "")).To(BeNil())
|
Expect(ftsSearchExpr("media_file", "")).To(BeNil())
|
||||||
Expect(ftsSearchExpr("media_file", " ")).To(BeNil())
|
Expect(ftsSearchExpr("media_file", " ")).To(BeNil())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("returns nil for empty quoted phrase", func() {
|
||||||
|
Expect(ftsSearchExpr("media_file", `""`)).To(BeNil())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = Describe("FTS5 Integration Search", func() {
|
var _ = Describe("FTS5 Integration Search", func() {
|
||||||
|
|||||||
@@ -107,6 +107,16 @@ var _ = Describe("Search Endpoints", func() {
|
|||||||
Expect(resp.SearchResult3.Artist[0].Id).ToNot(BeEmpty())
|
Expect(resp.SearchResult3.Artist[0].Id).ToNot(BeEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("returns all results when query is empty (OpenSubsonic)", func() {
|
||||||
|
resp := doReq("search3", "query", "")
|
||||||
|
|
||||||
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
||||||
|
Expect(resp.SearchResult3).ToNot(BeNil())
|
||||||
|
Expect(resp.SearchResult3.Artist).To(HaveLen(4))
|
||||||
|
Expect(resp.SearchResult3.Album).To(HaveLen(5))
|
||||||
|
Expect(resp.SearchResult3.Song).To(HaveLen(6))
|
||||||
|
})
|
||||||
|
|
||||||
It("finds across all entity types simultaneously", func() {
|
It("finds across all entity types simultaneously", func() {
|
||||||
// "Beatles" should match artist, albums, and songs by The Beatles
|
// "Beatles" should match artist, albums, and songs by The Beatles
|
||||||
resp := doReq("search3", "query", "Beatles")
|
resp := doReq("search3", "query", "Beatles")
|
||||||
|
|||||||
Reference in New Issue
Block a user