feat: enhance search with date-prioritized year filtering and true exact matching
Improve media item search functionality with two key enhancements: 1. Date-prioritized year filtering: - Prioritize date_published over copyright_year for year range queries - Fall back to copyright_year when date_published is NULL - Extract year from date_published timestamp for comparison 2. True exact search matching: - Replace ILIKE pattern matching with exact equality for quoted queries - Use search_query directly instead of wildcard pattern for exact matches - Remove SearchPattern parameter and related wildcard logic - Add COALESCE handling for author/series NULL values in exact matches These changes make year filtering more accurate with published dates and provide genuine exact matching when users wrap queries in quotes. Refs internal/database/queries/queries.sql:475, internal/services/search.go:62
This commit is contained in:
@@ -7259,9 +7259,17 @@ WHERE COALESCE(lv.is_visible, true) = true
|
||||
SELECT 1 FROM unnest(mi.tags_search) AS tag
|
||||
WHERE word_similarity($7, tag) > 0.3
|
||||
))
|
||||
-- Year range (exact)
|
||||
AND ($8 = 0 OR mi.copyright_year >= $8)
|
||||
AND ($9 = 0 OR mi.copyright_year <= $9)
|
||||
-- Year range (exact) - prioritize date_published, fallback to copyright_year
|
||||
AND (
|
||||
$8 = 0 OR
|
||||
EXTRACT(YEAR FROM mi.date_published) >= $8 OR
|
||||
(mi.date_published IS NULL AND mi.copyright_year >= $8)
|
||||
)
|
||||
AND (
|
||||
$9 = 0 OR
|
||||
EXTRACT(YEAR FROM mi.date_published) <= $9 OR
|
||||
(mi.date_published IS NULL AND mi.copyright_year <= $9)
|
||||
)
|
||||
-- Boolean (exact)
|
||||
AND ($10 = false OR mi.cover_image_path IS NOT NULL)
|
||||
-- Search query (fuzzy or exact based on quotes)
|
||||
@@ -7284,12 +7292,21 @@ WHERE COALESCE(lv.is_visible, true) = true
|
||||
)
|
||||
) OR
|
||||
-- Exact search (with quotes)
|
||||
-- Keeping old pattern commented out in case we want wildcard exact back. See search.go line 62
|
||||
-- sqlc.narg('is_exact_search') = true AND (
|
||||
-- mi.title ILIKE sqlc.narg('search_pattern') OR
|
||||
-- mi.author ILIKE sqlc.narg('search_pattern') OR
|
||||
-- mi.series ILIKE sqlc.narg('search_pattern') OR
|
||||
-- sqlc.narg('search_pattern') = ANY(mi.tags_search) OR
|
||||
-- sqlc.narg('search_pattern') = ANY(mi.contributors_search)
|
||||
-- )
|
||||
-- Exact search (with quotes) - true exact match, not substring
|
||||
$12 = true AND (
|
||||
mi.title ILIKE $13 OR
|
||||
mi.author ILIKE $13 OR
|
||||
mi.series ILIKE $13 OR
|
||||
$13 = ANY(mi.tags_search) OR
|
||||
$13 = ANY(mi.contributors_search)
|
||||
mi.title = $11 OR
|
||||
COALESCE(mi.author, '') = $11 OR
|
||||
COALESCE(mi.series, '') = $11 OR
|
||||
$11 = ANY(mi.tags_search) OR
|
||||
$11 = ANY(mi.contributors_search)
|
||||
)
|
||||
)
|
||||
ORDER BY
|
||||
@@ -7313,40 +7330,40 @@ ORDER BY
|
||||
END DESC,
|
||||
-- Secondary sort: user-specified sort parameter
|
||||
CASE
|
||||
WHEN $14 = 'title ASC' THEN mi.title
|
||||
WHEN $13 = 'title ASC' THEN mi.title
|
||||
ELSE ''
|
||||
END ASC,
|
||||
CASE
|
||||
WHEN $14 = 'title DESC' THEN mi.title
|
||||
WHEN $13 = 'title DESC' THEN mi.title
|
||||
ELSE ''
|
||||
END DESC,
|
||||
CASE
|
||||
WHEN $14 = 'author ASC' THEN COALESCE(mi.author, '')
|
||||
WHEN $13 = 'author ASC' THEN COALESCE(mi.author, '')
|
||||
ELSE ''
|
||||
END ASC,
|
||||
CASE
|
||||
WHEN $14 = 'author DESC' THEN COALESCE(mi.author, '')
|
||||
WHEN $13 = 'author DESC' THEN COALESCE(mi.author, '')
|
||||
ELSE ''
|
||||
END DESC,
|
||||
CASE
|
||||
WHEN $14 = 'created_at ASC' THEN mi.created_at
|
||||
WHEN $13 = 'created_at ASC' THEN mi.created_at
|
||||
ELSE '1970-01-01'::timestamp
|
||||
END ASC,
|
||||
CASE
|
||||
WHEN $14 = 'created_at DESC' THEN mi.created_at
|
||||
WHEN $13 = 'created_at DESC' THEN mi.created_at
|
||||
ELSE '1970-01-01'::timestamp
|
||||
END DESC,
|
||||
CASE
|
||||
WHEN $14 = 'page_count ASC' THEN COALESCE(mi.page_count::text, '0')
|
||||
WHEN $13 = 'page_count ASC' THEN COALESCE(mi.page_count::text, '0')
|
||||
ELSE ''
|
||||
END ASC,
|
||||
CASE
|
||||
WHEN $14 = 'page_count DESC' THEN COALESCE(mi.page_count::text, '0')
|
||||
WHEN $13 = 'page_count DESC' THEN COALESCE(mi.page_count::text, '0')
|
||||
ELSE ''
|
||||
END DESC,
|
||||
-- Tertiary sort: title (default fallback)
|
||||
mi.title ASC
|
||||
LIMIT $16 OFFSET $15
|
||||
LIMIT $15 OFFSET $14
|
||||
`
|
||||
|
||||
type SearchMediaItemsUnifiedParams struct {
|
||||
@@ -7362,7 +7379,6 @@ type SearchMediaItemsUnifiedParams struct {
|
||||
HasCover interface{} `db:"has_cover" json:"has_cover"`
|
||||
SearchQuery interface{} `db:"search_query" json:"search_query"`
|
||||
IsExactSearch interface{} `db:"is_exact_search" json:"is_exact_search"`
|
||||
SearchPattern pgtype.Text `db:"search_pattern" json:"search_pattern"`
|
||||
Sort interface{} `db:"sort" json:"sort"`
|
||||
Offset pgtype.Int4 `db:"offset" json:"offset"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
@@ -7431,7 +7447,6 @@ func (q *Queries) SearchMediaItemsUnified(ctx context.Context, arg SearchMediaIt
|
||||
arg.HasCover,
|
||||
arg.SearchQuery,
|
||||
arg.IsExactSearch,
|
||||
arg.SearchPattern,
|
||||
arg.Sort,
|
||||
arg.Offset,
|
||||
arg.Limit,
|
||||
|
||||
@@ -440,9 +440,17 @@ WHERE COALESCE(lv.is_visible, true) = true
|
||||
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'))
|
||||
-- Year range (exact) - prioritize date_published, fallback to copyright_year
|
||||
AND (
|
||||
sqlc.narg('year_min') = 0 OR
|
||||
EXTRACT(YEAR FROM mi.date_published) >= sqlc.narg('year_min') OR
|
||||
(mi.date_published IS NULL AND mi.copyright_year >= sqlc.narg('year_min'))
|
||||
)
|
||||
AND (
|
||||
sqlc.narg('year_max') = 0 OR
|
||||
EXTRACT(YEAR FROM mi.date_published) <= sqlc.narg('year_max') OR
|
||||
(mi.date_published IS NULL AND mi.copyright_year <= sqlc.narg('year_max'))
|
||||
)
|
||||
-- Boolean (exact)
|
||||
AND (sqlc.narg('has_cover') = false OR mi.cover_image_path IS NOT NULL)
|
||||
-- Search query (fuzzy or exact based on quotes)
|
||||
@@ -465,12 +473,21 @@ WHERE COALESCE(lv.is_visible, true) = true
|
||||
)
|
||||
) OR
|
||||
-- Exact search (with quotes)
|
||||
-- Keeping old pattern commented out in case we want wildcard exact back. See search.go line 62
|
||||
-- sqlc.narg('is_exact_search') = true AND (
|
||||
-- mi.title ILIKE sqlc.narg('search_pattern') OR
|
||||
-- mi.author ILIKE sqlc.narg('search_pattern') OR
|
||||
-- mi.series ILIKE sqlc.narg('search_pattern') OR
|
||||
-- sqlc.narg('search_pattern') = ANY(mi.tags_search) OR
|
||||
-- sqlc.narg('search_pattern') = ANY(mi.contributors_search)
|
||||
-- )
|
||||
-- Exact search (with quotes) - true exact match, not substring
|
||||
sqlc.narg('is_exact_search') = true AND (
|
||||
mi.title ILIKE sqlc.narg('search_pattern') OR
|
||||
mi.author ILIKE sqlc.narg('search_pattern') OR
|
||||
mi.series ILIKE sqlc.narg('search_pattern') OR
|
||||
sqlc.narg('search_pattern') = ANY(mi.tags_search) OR
|
||||
sqlc.narg('search_pattern') = ANY(mi.contributors_search)
|
||||
mi.title = sqlc.narg('search_query') OR
|
||||
COALESCE(mi.author, '') = sqlc.narg('search_query') OR
|
||||
COALESCE(mi.series, '') = sqlc.narg('search_query') OR
|
||||
sqlc.narg('search_query') = ANY(mi.tags_search) OR
|
||||
sqlc.narg('search_query') = ANY(mi.contributors_search)
|
||||
)
|
||||
)
|
||||
ORDER BY
|
||||
|
||||
Reference in New Issue
Block a user