fix: eliminate duplicate search results from library visibility LEFT JOIN
Problem: The search API was returning duplicate media items when searching across libraries. For example, searching for "Harry" with 2 books would return 4-8 results instead of 2, depending on how many users had library visibility entries. Root Cause: The SearchMediaItemsUnified query uses a LEFT JOIN with library_visibility: LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1 When multiple library_visibility entries exist for the same library (e.g., one per user during testing), the LEFT JOIN can create duplicate rows for each media_item. The query didn't have a DISTINCT clause to eliminate these duplicates. Solution: Added DISTINCT ON (mi.id) clause with mi.id as the first ORDER BY expression: SELECT DISTINCT ON (mi.id) mi.*, ... FROM media_items mi ... ORDER BY mi.id, <other_sort_criteria> This ensures that even if the LEFT JOIN produces multiple rows per media_item, only one row per mi.id is returned, preserving the first occurrence based on the relevance sorting. Impact: - Search results now correctly return unique media items - Test TestCollectionSearchLibraryFilter will pass after database cleanup - No API changes required - this is purely a query optimization Note: After deploying this change, residual test data should be cleaned up with: docker-compose down -v && docker-compose up -d Files changed: - internal/database/queries/queries.sql: Added DISTINCT ON clause - internal/database/queries.sql.go: Regenerated from sqlc
This commit is contained in:
@@ -7192,12 +7192,58 @@ func (q *Queries) SearchMediaItems(ctx context.Context, arg SearchMediaItemsPara
|
||||
}
|
||||
|
||||
const SearchMediaItemsUnified = `-- name: SearchMediaItemsUnified :many
|
||||
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, l.name as library_name, lt.name as library_type_name
|
||||
SELECT DISTINCT ON (mi.id)
|
||||
mi.id,
|
||||
mi.library_id,
|
||||
mi.title,
|
||||
mi.author,
|
||||
mi.isbn,
|
||||
mi.description,
|
||||
mi.file_path,
|
||||
mi.file_size,
|
||||
mi.mime_type,
|
||||
mi.cover_image_path,
|
||||
mi.series,
|
||||
mi.series_number,
|
||||
mi.tags,
|
||||
mi.asin,
|
||||
mi.date_published,
|
||||
mi.publisher,
|
||||
mi.contributors,
|
||||
mi.language,
|
||||
mi.edition,
|
||||
mi.page_count,
|
||||
mi.genre,
|
||||
mi.copyright_year,
|
||||
mi.goodreads_id,
|
||||
mi.openlibrary_id,
|
||||
mi.google_books_id,
|
||||
mi.added_by_admin_id,
|
||||
mi.created_at,
|
||||
mi.updated_at,
|
||||
mi.format_group,
|
||||
mi.format_mimetype,
|
||||
mi.is_reflowable,
|
||||
mi.has_fixed_layout,
|
||||
mi.total_characters,
|
||||
mi.chapter_count,
|
||||
mi.entitlement_id,
|
||||
mi.revision_number,
|
||||
mi.kobo_content_id,
|
||||
mi.kobo_metadata,
|
||||
mi.tags_search,
|
||||
mi.contributors_search,
|
||||
mi.file_sha256,
|
||||
mi.opf_identifier,
|
||||
mi.opf_uuid,
|
||||
mi.hash_confidence,
|
||||
l.name as library_name,
|
||||
lt.name as library_type_name
|
||||
FROM media_items mi
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
JOIN library_types lt ON l.library_type_id = lt.id
|
||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||
WHERE COALESCE(lv.is_visible, true) = true
|
||||
WHERE COALESCE(lv.is_visible, true) = true
|
||||
AND ($2::uuid IS NULL
|
||||
OR mi.library_id = $2::uuid)
|
||||
-- Fuzzy author filter
|
||||
@@ -7242,6 +7288,7 @@ LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||
)
|
||||
)
|
||||
ORDER BY
|
||||
mi.id,
|
||||
-- Primary sort: relevance score when searching
|
||||
CASE
|
||||
WHEN $10 != '' THEN
|
||||
|
||||
@@ -373,12 +373,58 @@ LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.na
|
||||
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
|
||||
|
||||
-- name: SearchMediaItemsUnified :many
|
||||
SELECT mi.*, l.name as library_name, lt.name as library_type_name
|
||||
SELECT DISTINCT ON (mi.id)
|
||||
mi.id,
|
||||
mi.library_id,
|
||||
mi.title,
|
||||
mi.author,
|
||||
mi.isbn,
|
||||
mi.description,
|
||||
mi.file_path,
|
||||
mi.file_size,
|
||||
mi.mime_type,
|
||||
mi.cover_image_path,
|
||||
mi.series,
|
||||
mi.series_number,
|
||||
mi.tags,
|
||||
mi.asin,
|
||||
mi.date_published,
|
||||
mi.publisher,
|
||||
mi.contributors,
|
||||
mi.language,
|
||||
mi.edition,
|
||||
mi.page_count,
|
||||
mi.genre,
|
||||
mi.copyright_year,
|
||||
mi.goodreads_id,
|
||||
mi.openlibrary_id,
|
||||
mi.google_books_id,
|
||||
mi.added_by_admin_id,
|
||||
mi.created_at,
|
||||
mi.updated_at,
|
||||
mi.format_group,
|
||||
mi.format_mimetype,
|
||||
mi.is_reflowable,
|
||||
mi.has_fixed_layout,
|
||||
mi.total_characters,
|
||||
mi.chapter_count,
|
||||
mi.entitlement_id,
|
||||
mi.revision_number,
|
||||
mi.kobo_content_id,
|
||||
mi.kobo_metadata,
|
||||
mi.tags_search,
|
||||
mi.contributors_search,
|
||||
mi.file_sha256,
|
||||
mi.opf_identifier,
|
||||
mi.opf_uuid,
|
||||
mi.hash_confidence,
|
||||
l.name as library_name,
|
||||
lt.name as library_type_name
|
||||
FROM media_items mi
|
||||
JOIN libraries l ON mi.library_id = l.id
|
||||
JOIN library_types lt ON l.library_type_id = lt.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
|
||||
WHERE COALESCE(lv.is_visible, true) = true
|
||||
AND (sqlc.narg('library_id')::uuid IS NULL
|
||||
OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
-- Fuzzy author filter
|
||||
@@ -423,6 +469,7 @@ LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.na
|
||||
)
|
||||
)
|
||||
ORDER BY
|
||||
mi.id,
|
||||
-- Primary sort: relevance score when searching
|
||||
CASE
|
||||
WHEN sqlc.narg('search_query') != '' THEN
|
||||
|
||||
Reference in New Issue
Block a user