diff --git a/internal/storage/enclosure.go b/internal/storage/enclosure.go index e212795f..ba0a69de 100644 --- a/internal/storage/enclosure.go +++ b/internal/storage/enclosure.go @@ -60,6 +60,51 @@ func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { return enclosures, nil } +func (s *Storage) GetEnclosuresForEntries(entryIDs []int64) (map[int64]model.EnclosureList, error) { + query := ` + SELECT + id, + user_id, + entry_id, + url, + size, + mime_type, + media_progression + FROM + enclosures + WHERE + entry_id = ANY($1) + ORDER BY id ASC + ` + + rows, err := s.db.Query(query, pq.Array(entryIDs)) + if err != nil { + return nil, fmt.Errorf("store: unable to fetch enclosures: %w", err) + } + defer rows.Close() + + enclosuresMap := make(map[int64]model.EnclosureList) + for rows.Next() { + var enclosure model.Enclosure + err := rows.Scan( + &enclosure.ID, + &enclosure.UserID, + &enclosure.EntryID, + &enclosure.URL, + &enclosure.Size, + &enclosure.MimeType, + &enclosure.MediaProgression, + ) + if err != nil { + return nil, fmt.Errorf("store: unable to scan enclosure row: %w", err) + } + + enclosuresMap[enclosure.EntryID] = append(enclosuresMap[enclosure.EntryID], &enclosure) + } + + return enclosuresMap, nil +} + func (s *Storage) GetEnclosure(enclosureID int64) (*model.Enclosure, error) { query := ` SELECT diff --git a/internal/storage/entry_query_builder.go b/internal/storage/entry_query_builder.go index 1245e1d4..e9f9692c 100644 --- a/internal/storage/entry_query_builder.go +++ b/internal/storage/entry_query_builder.go @@ -277,7 +277,6 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) { e.created_at, e.changed_at, e.tags, - (SELECT true FROM enclosures WHERE entry_id=e.id LIMIT 1) as has_enclosure, f.title as feed_title, f.feed_url, f.site_url, @@ -319,10 +318,12 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) { defer rows.Close() entries := make(model.Entries, 0) + entryMap := make(map[int64]*model.Entry) + var entryIDs []int64 + for rows.Next() { var iconID sql.NullInt64 var tz string - var hasEnclosure sql.NullBool entry := model.NewEntry() @@ -344,7 +345,6 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) { &entry.CreatedAt, &entry.ChangedAt, pq.Array(&entry.Tags), - &hasEnclosure, &entry.Feed.Title, &entry.Feed.FeedURL, &entry.Feed.SiteURL, @@ -368,20 +368,13 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) { return nil, fmt.Errorf("store: unable to fetch entry row: %v", err) } - if hasEnclosure.Valid && hasEnclosure.Bool && e.fetchEnclosures { - entry.Enclosures, err = e.store.GetEnclosures(entry.ID) - if err != nil { - return nil, fmt.Errorf("store: unable to fetch enclosures for entry #%d: %w", entry.ID, err) - } - } - if iconID.Valid { entry.Feed.Icon.IconID = iconID.Int64 } else { entry.Feed.Icon.IconID = 0 } - // Make sure that timestamp fields contains timezone information (API) + // Make sure that timestamp fields contain timezone information (API) entry.Date = timezone.Convert(tz, entry.Date) entry.CreatedAt = timezone.Convert(tz, entry.CreatedAt) entry.ChangedAt = timezone.Convert(tz, entry.ChangedAt) @@ -391,7 +384,23 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) { entry.Feed.UserID = entry.UserID entry.Feed.Icon.FeedID = entry.FeedID entry.Feed.Category.UserID = entry.UserID + entries = append(entries, entry) + entryMap[entry.ID] = entry + entryIDs = append(entryIDs, entry.ID) + } + + if e.fetchEnclosures && len(entryIDs) > 0 { + enclosures, err := e.store.GetEnclosuresForEntries(entryIDs) + if err != nil { + return nil, fmt.Errorf("store: unable to fetch enclosures: %w", err) + } + + for entryID, entryEnclosures := range enclosures { + if entry, exists := entryMap[entryID]; exists { + entry.Enclosures = entryEnclosures + } + } } return entries, nil