From 36ae781765b1bdf28922bfe0f8f1acb4d438840e Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Fri, 27 Mar 2026 18:07:41 -0400 Subject: [PATCH] fix: implement proper 3-state boolean logic for has_cover filter Fixed the SearchMediaItemsUnified query to properly handle the has_cover parameter in three states: - NULL (not specified): Show all books - TRUE: Show only books with cover images - FALSE: Show only books without cover images Changes: - Added explicit boolean casting (::bool) to sqlc.narg('has_cover') to resolve PostgreSQL type inference error (SQLSTATE 42P08) - Replaced single AND condition with OR'd logic to handle all three states without mutual exclusion - Used IS NULL check to detect when parameter is not specified - Used IS TRUE/IS FALSE to explicitly check boolean states The previous implementation had mutually exclusive AND conditions that prevented any records from matching when has_cover was explicitly set to TRUE or FALSE, causing the filter to block all searches. This fix resolves the issue where searches were returning 0 results regardless of other filter parameters when has_cover was included in the query. --- internal/database/queries.sql.go | 8 ++++++-- internal/database/queries/queries.sql | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/database/queries.sql.go b/internal/database/queries.sql.go index b054422..6ac8759 100644 --- a/internal/database/queries.sql.go +++ b/internal/database/queries.sql.go @@ -7271,7 +7271,11 @@ WHERE COALESCE(lv.is_visible, true) = true (mi.date_published IS NULL AND mi.copyright_year <= $9) ) -- Boolean (exact) - AND ($10 = false OR mi.cover_image_path IS NOT NULL) + AND ( + $10::bool IS NULL OR -- Not specified = show all + ($10::bool IS TRUE AND mi.cover_image_path IS NOT NULL) OR + ($10::bool IS FALSE AND mi.cover_image_path IS NULL) + ) -- Search query (fuzzy or exact based on quotes) AND ( $11 = '' OR @@ -7376,7 +7380,7 @@ type SearchMediaItemsUnifiedParams struct { TagsFilter interface{} `db:"tags_filter" json:"tags_filter"` YearMin interface{} `db:"year_min" json:"year_min"` YearMax interface{} `db:"year_max" json:"year_max"` - HasCover interface{} `db:"has_cover" json:"has_cover"` + HasCover pgtype.Bool `db:"has_cover" json:"has_cover"` SearchQuery interface{} `db:"search_query" json:"search_query"` IsExactSearch interface{} `db:"is_exact_search" json:"is_exact_search"` Sort interface{} `db:"sort" json:"sort"` diff --git a/internal/database/queries/queries.sql b/internal/database/queries/queries.sql index 78808ee..f1ecb95 100644 --- a/internal/database/queries/queries.sql +++ b/internal/database/queries/queries.sql @@ -452,7 +452,11 @@ WHERE COALESCE(lv.is_visible, true) = true (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) + AND ( + sqlc.narg('has_cover')::bool IS NULL OR -- Not specified = show all + (sqlc.narg('has_cover')::bool IS TRUE AND mi.cover_image_path IS NOT NULL) OR + (sqlc.narg('has_cover')::bool IS FALSE AND mi.cover_image_path IS NULL) + ) -- Search query (fuzzy or exact based on quotes) AND ( sqlc.narg('search_query') = '' OR