refactor(db): GetCollectionItemsForDashboard embeds the media row; excluded filter moves into SQL; third converter dies
SELECT mi.*, ci.excluded forced every caller to filter tombstone exclusions in Go over a 69-field row, then a 45-field hand converter (getCollectionItemsRowToMediaItems) copied it field-by-field into MediaItems for BuildSections. The query now filters (ci.excluded IS NOT TRUE) itself and returns sqlc.embed(mi) — callers consume .MediaItems directly, the Go filter loops die in both collection-detail ladders (JSON + SSR), and the converter is deleted. Pins, H1/H3 tests, unit suites green.
This commit is contained in:
1 parent
18727fef93
commit
77cf0b827b
7 files changed
+102
-218
No files matched your search
@@ -174,6 +174,9 @@ type Querier interface {
|
|||||||
GetCollection(ctx context.Context, id pgtype.UUID) (Collections, error)
|
GetCollection(ctx context.Context, id pgtype.UUID) (Collections, error)
|
||||||
// Get collection items
|
// Get collection items
|
||||||
GetCollectionItems(ctx context.Context, collectionID pgtype.UUID) ([]GetCollectionItemsRow, error)
|
GetCollectionItems(ctx context.Context, collectionID pgtype.UUID) ([]GetCollectionItemsRow, error)
|
||||||
|
// The excluded filter lives here (was: callers filtered ci.excluded in
|
||||||
|
// Go over a 69-field row + a 45-field converter to MediaItems; embed
|
||||||
|
// returns the real row so neither exists).
|
||||||
GetCollectionItemsForDashboard(ctx context.Context, arg GetCollectionItemsForDashboardParams) ([]GetCollectionItemsForDashboardRow, error)
|
GetCollectionItemsForDashboard(ctx context.Context, arg GetCollectionItemsForDashboardParams) ([]GetCollectionItemsForDashboardRow, error)
|
||||||
// Get collection with book count
|
// Get collection with book count
|
||||||
GetCollectionWithBookCount(ctx context.Context, id pgtype.UUID) (GetCollectionWithBookCountRow, error)
|
GetCollectionWithBookCount(ctx context.Context, id pgtype.UUID) (GetCollectionWithBookCountRow, error)
|
||||||
|
|||||||
@@ -2832,12 +2832,13 @@ func (q *Queries) GetCollectionItems(ctx context.Context, collectionID pgtype.UU
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GetCollectionItemsForDashboard = `-- name: GetCollectionItemsForDashboard :many
|
const GetCollectionItemsForDashboard = `-- name: GetCollectionItemsForDashboard :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.narrators, mi.abridged, 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.imported_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.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.metadata_overrides, mi.missing_scan_count, mi.archived_at, mi.chapter_metadata, mi.library_type_name, mi.audio_metadata, mi.tags_search, mi.contributors_search, mi.narrators_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, ci.excluded FROM media_items mi
|
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.narrators, mi.abridged, 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.imported_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.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.metadata_overrides, mi.missing_scan_count, mi.archived_at, mi.chapter_metadata, mi.library_type_name, mi.audio_metadata, mi.tags_search, mi.contributors_search, mi.narrators_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
||||||
INNER JOIN collection_items ci ON ci.media_item_id = mi.id
|
INNER JOIN collection_items ci ON ci.media_item_id = mi.id
|
||||||
WHERE ci.collection_id = $1
|
WHERE ci.collection_id = $1
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (ci.excluded IS NOT TRUE)
|
||||||
ORDER BY ci.added_at DESC
|
ORDER BY ci.added_at DESC
|
||||||
LIMIT $3
|
LIMIT $3
|
||||||
`
|
`
|
||||||
@@ -2849,77 +2850,12 @@ type GetCollectionItemsForDashboardParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetCollectionItemsForDashboardRow struct {
|
type GetCollectionItemsForDashboardRow struct {
|
||||||
ID pgtype.UUID `db:"id" json:"id"`
|
MediaItems MediaItems `db:"media_items" json:"media_items"`
|
||||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
|
||||||
Title string `db:"title" json:"title"`
|
|
||||||
Author pgtype.Text `db:"author" json:"author"`
|
|
||||||
Isbn pgtype.Text `db:"isbn" json:"isbn"`
|
|
||||||
Description pgtype.Text `db:"description" json:"description"`
|
|
||||||
FilePath string `db:"file_path" json:"file_path"`
|
|
||||||
FileSize pgtype.Int8 `db:"file_size" json:"file_size"`
|
|
||||||
MimeType pgtype.Text `db:"mime_type" json:"mime_type"`
|
|
||||||
CoverImagePath pgtype.Text `db:"cover_image_path" json:"cover_image_path"`
|
|
||||||
Series pgtype.Text `db:"series" json:"series"`
|
|
||||||
SeriesNumber pgtype.Int4 `db:"series_number" json:"series_number"`
|
|
||||||
Tags []string `db:"tags" json:"tags"`
|
|
||||||
Narrators []string `db:"narrators" json:"narrators"`
|
|
||||||
Abridged bool `db:"abridged" json:"abridged"`
|
|
||||||
Asin pgtype.Text `db:"asin" json:"asin"`
|
|
||||||
DatePublished pgtype.Date `db:"date_published" json:"date_published"`
|
|
||||||
Publisher pgtype.Text `db:"publisher" json:"publisher"`
|
|
||||||
Contributors []string `db:"contributors" json:"contributors"`
|
|
||||||
Language pgtype.Text `db:"language" json:"language"`
|
|
||||||
Edition pgtype.Text `db:"edition" json:"edition"`
|
|
||||||
PageCount pgtype.Int4 `db:"page_count" json:"page_count"`
|
|
||||||
Genre pgtype.Text `db:"genre" json:"genre"`
|
|
||||||
CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"`
|
|
||||||
GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"`
|
|
||||||
OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"`
|
|
||||||
GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"`
|
|
||||||
AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"`
|
|
||||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
|
||||||
ImportedAt pgtype.Timestamptz `db:"imported_at" json:"imported_at"`
|
|
||||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
|
||||||
FormatGroup string `db:"format_group" json:"format_group"`
|
|
||||||
FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"`
|
|
||||||
IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"`
|
|
||||||
HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"`
|
|
||||||
TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"`
|
|
||||||
ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"`
|
|
||||||
EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"`
|
|
||||||
RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"`
|
|
||||||
KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"`
|
|
||||||
KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"`
|
|
||||||
MangaType pgtype.Text `db:"manga_type" json:"manga_type"`
|
|
||||||
ReadingDirection pgtype.Text `db:"reading_direction" json:"reading_direction"`
|
|
||||||
SeriesCount pgtype.Int4 `db:"series_count" json:"series_count"`
|
|
||||||
Volume pgtype.Int4 `db:"volume" json:"volume"`
|
|
||||||
Imprint pgtype.Text `db:"imprint" json:"imprint"`
|
|
||||||
AgeRating pgtype.Text `db:"age_rating" json:"age_rating"`
|
|
||||||
WebUrl pgtype.Text `db:"web_url" json:"web_url"`
|
|
||||||
StoryArc pgtype.Text `db:"story_arc" json:"story_arc"`
|
|
||||||
IsBlackAndWhite pgtype.Bool `db:"is_black_and_white" json:"is_black_and_white"`
|
|
||||||
MetadataNotes pgtype.Text `db:"metadata_notes" json:"metadata_notes"`
|
|
||||||
CommunityRating pgtype.Float8 `db:"community_rating" json:"community_rating"`
|
|
||||||
AlternateInfo []byte `db:"alternate_info" json:"alternate_info"`
|
|
||||||
ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"`
|
|
||||||
Summary pgtype.Text `db:"summary" json:"summary"`
|
|
||||||
MetadataOverrides []string `db:"metadata_overrides" json:"metadata_overrides"`
|
|
||||||
MissingScanCount int32 `db:"missing_scan_count" json:"missing_scan_count"`
|
|
||||||
ArchivedAt pgtype.Timestamptz `db:"archived_at" json:"archived_at"`
|
|
||||||
ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"`
|
|
||||||
LibraryTypeName pgtype.Text `db:"library_type_name" json:"library_type_name"`
|
|
||||||
AudioMetadata []byte `db:"audio_metadata" json:"audio_metadata"`
|
|
||||||
TagsSearch []string `db:"tags_search" json:"tags_search"`
|
|
||||||
ContributorsSearch []string `db:"contributors_search" json:"contributors_search"`
|
|
||||||
NarratorsSearch []string `db:"narrators_search" json:"narrators_search"`
|
|
||||||
FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"`
|
|
||||||
OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"`
|
|
||||||
OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"`
|
|
||||||
HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"`
|
|
||||||
Excluded pgtype.Bool `db:"excluded" json:"excluded"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The excluded filter lives here (was: callers filtered ci.excluded in
|
||||||
|
// Go over a 69-field row + a 45-field converter to MediaItems; embed
|
||||||
|
// returns the real row so neither exists).
|
||||||
func (q *Queries) GetCollectionItemsForDashboard(ctx context.Context, arg GetCollectionItemsForDashboardParams) ([]GetCollectionItemsForDashboardRow, error) {
|
func (q *Queries) GetCollectionItemsForDashboard(ctx context.Context, arg GetCollectionItemsForDashboardParams) ([]GetCollectionItemsForDashboardRow, error) {
|
||||||
rows, err := q.db.Query(ctx, GetCollectionItemsForDashboard, arg.CollectionID, arg.LibraryID, arg.Limit)
|
rows, err := q.db.Query(ctx, GetCollectionItemsForDashboard, arg.CollectionID, arg.LibraryID, arg.Limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -2930,75 +2866,74 @@ func (q *Queries) GetCollectionItemsForDashboard(ctx context.Context, arg GetCol
|
|||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i GetCollectionItemsForDashboardRow
|
var i GetCollectionItemsForDashboardRow
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.ID,
|
&i.MediaItems.ID,
|
||||||
&i.LibraryID,
|
&i.MediaItems.LibraryID,
|
||||||
&i.Title,
|
&i.MediaItems.Title,
|
||||||
&i.Author,
|
&i.MediaItems.Author,
|
||||||
&i.Isbn,
|
&i.MediaItems.Isbn,
|
||||||
&i.Description,
|
&i.MediaItems.Description,
|
||||||
&i.FilePath,
|
&i.MediaItems.FilePath,
|
||||||
&i.FileSize,
|
&i.MediaItems.FileSize,
|
||||||
&i.MimeType,
|
&i.MediaItems.MimeType,
|
||||||
&i.CoverImagePath,
|
&i.MediaItems.CoverImagePath,
|
||||||
&i.Series,
|
&i.MediaItems.Series,
|
||||||
&i.SeriesNumber,
|
&i.MediaItems.SeriesNumber,
|
||||||
&i.Tags,
|
&i.MediaItems.Tags,
|
||||||
&i.Narrators,
|
&i.MediaItems.Narrators,
|
||||||
&i.Abridged,
|
&i.MediaItems.Abridged,
|
||||||
&i.Asin,
|
&i.MediaItems.Asin,
|
||||||
&i.DatePublished,
|
&i.MediaItems.DatePublished,
|
||||||
&i.Publisher,
|
&i.MediaItems.Publisher,
|
||||||
&i.Contributors,
|
&i.MediaItems.Contributors,
|
||||||
&i.Language,
|
&i.MediaItems.Language,
|
||||||
&i.Edition,
|
&i.MediaItems.Edition,
|
||||||
&i.PageCount,
|
&i.MediaItems.PageCount,
|
||||||
&i.Genre,
|
&i.MediaItems.Genre,
|
||||||
&i.CopyrightYear,
|
&i.MediaItems.CopyrightYear,
|
||||||
&i.GoodreadsID,
|
&i.MediaItems.GoodreadsID,
|
||||||
&i.OpenlibraryID,
|
&i.MediaItems.OpenlibraryID,
|
||||||
&i.GoogleBooksID,
|
&i.MediaItems.GoogleBooksID,
|
||||||
&i.AddedByAdminID,
|
&i.MediaItems.AddedByAdminID,
|
||||||
&i.CreatedAt,
|
&i.MediaItems.CreatedAt,
|
||||||
&i.ImportedAt,
|
&i.MediaItems.ImportedAt,
|
||||||
&i.UpdatedAt,
|
&i.MediaItems.UpdatedAt,
|
||||||
&i.FormatGroup,
|
&i.MediaItems.FormatGroup,
|
||||||
&i.FormatMimetype,
|
&i.MediaItems.FormatMimetype,
|
||||||
&i.IsReflowable,
|
&i.MediaItems.IsReflowable,
|
||||||
&i.HasFixedLayout,
|
&i.MediaItems.HasFixedLayout,
|
||||||
&i.TotalCharacters,
|
&i.MediaItems.TotalCharacters,
|
||||||
&i.ChapterCount,
|
&i.MediaItems.ChapterCount,
|
||||||
&i.EntitlementID,
|
&i.MediaItems.EntitlementID,
|
||||||
&i.RevisionNumber,
|
&i.MediaItems.RevisionNumber,
|
||||||
&i.KoboContentID,
|
&i.MediaItems.KoboContentID,
|
||||||
&i.KoboMetadata,
|
&i.MediaItems.KoboMetadata,
|
||||||
&i.MangaType,
|
&i.MediaItems.MangaType,
|
||||||
&i.ReadingDirection,
|
&i.MediaItems.ReadingDirection,
|
||||||
&i.SeriesCount,
|
&i.MediaItems.SeriesCount,
|
||||||
&i.Volume,
|
&i.MediaItems.Volume,
|
||||||
&i.Imprint,
|
&i.MediaItems.Imprint,
|
||||||
&i.AgeRating,
|
&i.MediaItems.AgeRating,
|
||||||
&i.WebUrl,
|
&i.MediaItems.WebUrl,
|
||||||
&i.StoryArc,
|
&i.MediaItems.StoryArc,
|
||||||
&i.IsBlackAndWhite,
|
&i.MediaItems.IsBlackAndWhite,
|
||||||
&i.MetadataNotes,
|
&i.MediaItems.MetadataNotes,
|
||||||
&i.CommunityRating,
|
&i.MediaItems.CommunityRating,
|
||||||
&i.AlternateInfo,
|
&i.MediaItems.AlternateInfo,
|
||||||
&i.ScanInformation,
|
&i.MediaItems.ScanInformation,
|
||||||
&i.Summary,
|
&i.MediaItems.Summary,
|
||||||
&i.MetadataOverrides,
|
&i.MediaItems.MetadataOverrides,
|
||||||
&i.MissingScanCount,
|
&i.MediaItems.MissingScanCount,
|
||||||
&i.ArchivedAt,
|
&i.MediaItems.ArchivedAt,
|
||||||
&i.ChapterMetadata,
|
&i.MediaItems.ChapterMetadata,
|
||||||
&i.LibraryTypeName,
|
&i.MediaItems.LibraryTypeName,
|
||||||
&i.AudioMetadata,
|
&i.MediaItems.AudioMetadata,
|
||||||
&i.TagsSearch,
|
&i.MediaItems.TagsSearch,
|
||||||
&i.ContributorsSearch,
|
&i.MediaItems.ContributorsSearch,
|
||||||
&i.NarratorsSearch,
|
&i.MediaItems.NarratorsSearch,
|
||||||
&i.FileSha256,
|
&i.MediaItems.FileSha256,
|
||||||
&i.OpfIdentifier,
|
&i.MediaItems.OpfIdentifier,
|
||||||
&i.OpfUuid,
|
&i.MediaItems.OpfUuid,
|
||||||
&i.HashConfidence,
|
&i.MediaItems.HashConfidence,
|
||||||
&i.Excluded,
|
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2791,12 +2791,16 @@ ORDER BY mi.created_at DESC
|
|||||||
LIMIT sqlc.narg('limit');
|
LIMIT sqlc.narg('limit');
|
||||||
|
|
||||||
-- name: GetCollectionItemsForDashboard :many
|
-- name: GetCollectionItemsForDashboard :many
|
||||||
SELECT mi.*, ci.excluded FROM media_items mi
|
-- The excluded filter lives here (was: callers filtered ci.excluded in
|
||||||
|
-- Go over a 69-field row + a 45-field converter to MediaItems; embed
|
||||||
|
-- returns the real row so neither exists).
|
||||||
|
SELECT sqlc.embed(mi) FROM media_items mi
|
||||||
INNER JOIN collection_items ci ON ci.media_item_id = mi.id
|
INNER JOIN collection_items ci ON ci.media_item_id = mi.id
|
||||||
WHERE ci.collection_id = sqlc.narg('collection_id')
|
WHERE ci.collection_id = sqlc.narg('collection_id')
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (ci.excluded IS NOT TRUE)
|
||||||
ORDER BY ci.added_at DESC
|
ORDER BY ci.added_at DESC
|
||||||
LIMIT sqlc.narg('limit');
|
LIMIT sqlc.narg('limit');
|
||||||
|
|
||||||
|
|||||||
+11
-10
@@ -125,20 +125,21 @@ func BookCardsFromMediaItems(rows []database.MediaItems, libTypes map[string]str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BookCardsFromCollectionDashRows builds cards from the
|
// BookCardsFromCollectionDashRows builds cards from the
|
||||||
// collection-items join (GetCollectionItemsForDashboard). The row's ID
|
// collection-items join (GetCollectionItemsForDashboard). The query
|
||||||
// IS the media item id. The Excluded filter stays at call sites — only
|
// embeds the media row and filters excluded rows in SQL, so the adapter
|
||||||
// the two collection-detail surfaces exclude tombstones.
|
// reads through the embed.
|
||||||
func BookCardsFromCollectionDashRows(rows []database.GetCollectionItemsForDashboardRow, libTypes map[string]string) []BookInfo {
|
func BookCardsFromCollectionDashRows(rows []database.GetCollectionItemsForDashboardRow, libTypes map[string]string) []BookInfo {
|
||||||
cards := make([]BookInfo, 0, len(rows))
|
cards := make([]BookInfo, 0, len(rows))
|
||||||
for _, r := range rows {
|
for _, r := range rows {
|
||||||
|
mi := r.MediaItems
|
||||||
cards = append(cards, buildBookCard(cardSource{
|
cards = append(cards, buildBookCard(cardSource{
|
||||||
ID: uuid.UUID(r.ID.Bytes),
|
ID: uuid.UUID(mi.ID.Bytes),
|
||||||
Title: r.Title,
|
Title: mi.Title,
|
||||||
Author: utils.PgTextString(r.Author),
|
Author: utils.PgTextString(mi.Author),
|
||||||
LibraryID: r.LibraryID,
|
LibraryID: mi.LibraryID,
|
||||||
CoverPath: r.CoverImagePath,
|
CoverPath: mi.CoverImagePath,
|
||||||
FormatGroup: r.FormatGroup,
|
FormatGroup: mi.FormatGroup,
|
||||||
LibraryType: libTypes[uuid.UUID(r.LibraryID.Bytes).String()],
|
LibraryType: libTypes[uuid.UUID(mi.LibraryID.Bytes).String()],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
return cards
|
return cards
|
||||||
|
|||||||
@@ -293,13 +293,9 @@ func (h *CollectionHandler) GetCollection(c *echo.Context) error {
|
|||||||
if collErr != nil {
|
if collErr != nil {
|
||||||
bookList = BookCardsFromCollectionDashRows(nil, libTypes)
|
bookList = BookCardsFromCollectionDashRows(nil, libTypes)
|
||||||
} else {
|
} else {
|
||||||
var validItems []database.GetCollectionItemsForDashboardRow
|
// The query filters excluded rows in SQL; cards build straight
|
||||||
for _, item := range collItems {
|
// from the embedded media rows.
|
||||||
if !item.Excluded.Valid || !item.Excluded.Bool {
|
bookList = BookCardsFromCollectionDashRows(collItems, libTypes)
|
||||||
validItems = append(validItems, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bookList = BookCardsFromCollectionDashRows(validItems, libTypes)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
books, booksErr := h.GetCollectionBooksData(c, collectionID)
|
books, booksErr := h.GetCollectionBooksData(c, collectionID)
|
||||||
|
|||||||
@@ -752,13 +752,8 @@ func registerFrontendRoutes(cfg *Config) {
|
|||||||
if collErr != nil {
|
if collErr != nil {
|
||||||
books = handlers.BookCardsFromCollectionDashRows(nil, libTypes)
|
books = handlers.BookCardsFromCollectionDashRows(nil, libTypes)
|
||||||
} else {
|
} else {
|
||||||
var validItems []database.GetCollectionItemsForDashboardRow
|
// The query filters excluded rows in SQL.
|
||||||
for _, item := range collItems {
|
books = handlers.BookCardsFromCollectionDashRows(collItems, libTypes)
|
||||||
if !item.Excluded.Valid || !item.Excluded.Bool {
|
|
||||||
validItems = append(validItems, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
books = handlers.BookCardsFromCollectionDashRows(validItems, libTypes)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
collItems, collErr := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
|
collItems, collErr := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bookhoard/internal/database"
|
"bookhoard/internal/database"
|
||||||
"bookhoard/internal/utils"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -13,55 +12,6 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getCollectionItemsRowToMediaItems(item database.GetCollectionItemsForDashboardRow) database.MediaItems {
|
|
||||||
return database.MediaItems{
|
|
||||||
ID: item.ID,
|
|
||||||
LibraryID: item.LibraryID,
|
|
||||||
Title: item.Title,
|
|
||||||
Author: item.Author,
|
|
||||||
Isbn: item.Isbn,
|
|
||||||
Description: item.Description,
|
|
||||||
FilePath: utils.ResolveMediaURL(item.LibraryID, pgtype.Text{String: item.FilePath, Valid: true}),
|
|
||||||
FileSize: item.FileSize,
|
|
||||||
MimeType: item.MimeType,
|
|
||||||
CoverImagePath: pgtype.Text{String: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath), Valid: true},
|
|
||||||
Series: item.Series,
|
|
||||||
SeriesNumber: item.SeriesNumber,
|
|
||||||
Tags: item.Tags,
|
|
||||||
Asin: item.Asin,
|
|
||||||
DatePublished: item.DatePublished,
|
|
||||||
Publisher: item.Publisher,
|
|
||||||
Contributors: item.Contributors,
|
|
||||||
Language: item.Language,
|
|
||||||
Edition: item.Edition,
|
|
||||||
PageCount: item.PageCount,
|
|
||||||
Genre: item.Genre,
|
|
||||||
CopyrightYear: item.CopyrightYear,
|
|
||||||
GoodreadsID: item.GoodreadsID,
|
|
||||||
OpenlibraryID: item.OpenlibraryID,
|
|
||||||
GoogleBooksID: item.GoogleBooksID,
|
|
||||||
AddedByAdminID: item.AddedByAdminID,
|
|
||||||
CreatedAt: item.CreatedAt,
|
|
||||||
UpdatedAt: item.UpdatedAt,
|
|
||||||
FormatGroup: item.FormatGroup,
|
|
||||||
FormatMimetype: item.FormatMimetype,
|
|
||||||
IsReflowable: item.IsReflowable,
|
|
||||||
HasFixedLayout: item.HasFixedLayout,
|
|
||||||
TotalCharacters: item.TotalCharacters,
|
|
||||||
ChapterCount: item.ChapterCount,
|
|
||||||
EntitlementID: item.EntitlementID,
|
|
||||||
RevisionNumber: item.RevisionNumber,
|
|
||||||
KoboContentID: item.KoboContentID,
|
|
||||||
KoboMetadata: item.KoboMetadata,
|
|
||||||
TagsSearch: item.TagsSearch,
|
|
||||||
ContributorsSearch: item.ContributorsSearch,
|
|
||||||
FileSha256: item.FileSha256,
|
|
||||||
OpfIdentifier: item.OpfIdentifier,
|
|
||||||
OpfUuid: item.OpfUuid,
|
|
||||||
HashConfidence: item.HashConfidence,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DashboardService struct {
|
type DashboardService struct {
|
||||||
db *database.Queries
|
db *database.Queries
|
||||||
collectionService *CollectionService
|
collectionService *CollectionService
|
||||||
@@ -277,11 +227,11 @@ func (s *DashboardService) getUserCollectionItems(ctx context.Context, coll data
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The query filters excluded rows in SQL and embeds the media row,
|
||||||
|
// so the items are MediaItems as-is.
|
||||||
var manualNonExcluded []database.MediaItems
|
var manualNonExcluded []database.MediaItems
|
||||||
for _, item := range manualItems {
|
for _, row := range manualItems {
|
||||||
if !item.Excluded.Valid || !item.Excluded.Bool {
|
manualNonExcluded = append(manualNonExcluded, row.MediaItems)
|
||||||
manualNonExcluded = append(manualNonExcluded, getCollectionItemsRowToMediaItems(item))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var autoItems []database.MediaItems
|
var autoItems []database.MediaItems
|
||||||
|
|||||||
Reference in new issue
Block a user