1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-06-27 16:36:00 +00:00

feat(storage): reduce the number of SQL queries when fetching entry enclosures

This commit is contained in:
Frédéric Guillot 2025-03-23 13:23:35 -07:00
parent 984a66b590
commit 82a6fe64ae
2 changed files with 65 additions and 11 deletions

View file

@ -60,6 +60,51 @@ func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) {
return enclosures, nil 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) { func (s *Storage) GetEnclosure(enclosureID int64) (*model.Enclosure, error) {
query := ` query := `
SELECT SELECT

View file

@ -277,7 +277,6 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) {
e.created_at, e.created_at,
e.changed_at, e.changed_at,
e.tags, e.tags,
(SELECT true FROM enclosures WHERE entry_id=e.id LIMIT 1) as has_enclosure,
f.title as feed_title, f.title as feed_title,
f.feed_url, f.feed_url,
f.site_url, f.site_url,
@ -319,10 +318,12 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) {
defer rows.Close() defer rows.Close()
entries := make(model.Entries, 0) entries := make(model.Entries, 0)
entryMap := make(map[int64]*model.Entry)
var entryIDs []int64
for rows.Next() { for rows.Next() {
var iconID sql.NullInt64 var iconID sql.NullInt64
var tz string var tz string
var hasEnclosure sql.NullBool
entry := model.NewEntry() entry := model.NewEntry()
@ -344,7 +345,6 @@ func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) {
&entry.CreatedAt, &entry.CreatedAt,
&entry.ChangedAt, &entry.ChangedAt,
pq.Array(&entry.Tags), pq.Array(&entry.Tags),
&hasEnclosure,
&entry.Feed.Title, &entry.Feed.Title,
&entry.Feed.FeedURL, &entry.Feed.FeedURL,
&entry.Feed.SiteURL, &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) 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 { if iconID.Valid {
entry.Feed.Icon.IconID = iconID.Int64 entry.Feed.Icon.IconID = iconID.Int64
} else { } else {
entry.Feed.Icon.IconID = 0 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.Date = timezone.Convert(tz, entry.Date)
entry.CreatedAt = timezone.Convert(tz, entry.CreatedAt) entry.CreatedAt = timezone.Convert(tz, entry.CreatedAt)
entry.ChangedAt = timezone.Convert(tz, entry.ChangedAt) 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.UserID = entry.UserID
entry.Feed.Icon.FeedID = entry.FeedID entry.Feed.Icon.FeedID = entry.FeedID
entry.Feed.Category.UserID = entry.UserID entry.Feed.Category.UserID = entry.UserID
entries = append(entries, entry) 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 return entries, nil