feat: add fuzzy tags_filter to search query
- Add tags_filter parameter to SearchMediaItemsUnified - Add EXISTS clause with word_similarity() for fuzzy tag matching - Add tag similarity scoring to ORDER BY clause (GREATEST function) - Add SearchTagsValues query for autocomplete with ::TEXT cast - Keep genre_filter for backward compatibility - Regenerate Go code with sqlc generate This enables filtering books by tags (from Calibre) instead of genre, which is always NULL for imported books. Uses fuzzy matching consistent with author/series filters, with best matches sorted first. Relates to IMPLEMENTATION_TAGS_FILTER.md Phase 1
This commit is contained in:
@@ -435,6 +435,11 @@ WHERE COALESCE(lv.is_visible, true) = true
|
||||
AND (sqlc.narg('genre_filter') = '' OR word_similarity(sqlc.narg('genre_filter'), COALESCE(mi.genre, '')) > 0.3)
|
||||
-- Fuzzy language filter
|
||||
AND (sqlc.narg('language_filter') = '' OR word_similarity(sqlc.narg('language_filter'), COALESCE(mi.language, '')) > 0.3)
|
||||
-- Tags filter (NEW - fuzzy match against tags array)
|
||||
AND (sqlc.narg('tags_filter') = '' OR EXISTS (
|
||||
SELECT 1 FROM unnest(mi.tags_search) AS tag
|
||||
WHERE word_similarity(sqlc.narg('tags_filter'), tag) > 0.3
|
||||
))
|
||||
-- Year range (exact)
|
||||
AND (sqlc.narg('year_min') = 0 OR mi.copyright_year >= sqlc.narg('year_min'))
|
||||
AND (sqlc.narg('year_max') = 0 OR mi.copyright_year <= sqlc.narg('year_max'))
|
||||
@@ -481,7 +486,9 @@ ORDER BY
|
||||
word_similarity(sqlc.narg('search_query'), COALESCE(mi.author, ''))
|
||||
ELSE 0 END,
|
||||
word_similarity(sqlc.narg('author_filter'), COALESCE(mi.author, '')),
|
||||
word_similarity(sqlc.narg('genre_filter'), COALESCE(mi.genre, ''))
|
||||
word_similarity(sqlc.narg('genre_filter'), COALESCE(mi.genre, '')),
|
||||
(SELECT MAX(word_similarity(sqlc.narg('tags_filter'), tag))
|
||||
FROM unnest(mi.tags_search) AS tag)
|
||||
)
|
||||
ELSE 0
|
||||
END DESC,
|
||||
@@ -556,6 +563,22 @@ GROUP BY mi.genre, word_similarity(sqlc.narg('search_query'), COALESCE(mi.genre,
|
||||
ORDER BY score DESC, count DESC
|
||||
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
|
||||
|
||||
-- name: SearchTagsValues :many
|
||||
SELECT
|
||||
unnest(mi.tags_search)::TEXT as value,
|
||||
COUNT(*) as count,
|
||||
word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search))::float8 as score
|
||||
FROM media_items mi
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||
WHERE COALESCE(lv.is_visible, true) = true
|
||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
AND unnest(mi.tags_search) IS NOT NULL
|
||||
AND word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search)) > 0.3
|
||||
GROUP BY unnest(mi.tags_search), word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search))::float8
|
||||
ORDER BY word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search))::float8 DESC, COUNT DESC
|
||||
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
|
||||
|
||||
-- name: SearchSeriesValues :many
|
||||
SELECT
|
||||
mi.series as value,
|
||||
|
||||
Reference in New Issue
Block a user