Chore: Optimize tag retrieval by batching message IDs in List and Search functions

This commit is contained in:
Ralph Slooten
2026-05-09 16:27:58 +12:00
parent 40c5936f79
commit f11fc1ffe0
3 changed files with 63 additions and 4 deletions

View File

@@ -278,8 +278,19 @@ func List(start int, beforeTS int64, limit int) ([]MessageSummary, error) {
}
// set tags for listed messages only
for i, m := range results {
results[i].Tags = getMessageTags(m.ID)
if len(results) > 0 {
ids := make([]string, len(results))
for i, m := range results {
ids[i] = m.ID
}
tagMap := getTagsForIDs(ids)
for i, m := range results {
if tags, ok := tagMap[m.ID]; ok {
results[i].Tags = tags
} else {
results[i].Tags = []string{}
}
}
}
dbLastAction = time.Now()

View File

@@ -86,8 +86,19 @@ func Search(search, timezone string, start int, beforeTS int64, limit int) ([]Me
}
// set tags for listed messages only
for i, m := range results {
results[i].Tags = getMessageTags(m.ID)
if len(results) > 0 {
ids := make([]string, len(results))
for i, m := range results {
ids[i] = m.ID
}
tagMap := getTagsForIDs(ids)
for i, m := range results {
if tags, ok := tagMap[m.ID]; ok {
results[i].Tags = tags
} else {
results[i].Tags = []string{}
}
}
}
elapsed := time.Since(tsStart)

View File

@@ -340,6 +340,43 @@ func (d Metadata) tagsFromPlusAddresses() []string {
return tools.SetTagCasing(tags)
}
// getTagsForIDs fetches tags for a set of message IDs in a single query,
// returning a map of message ID to tag names.
func getTagsForIDs(ids []string) map[string][]string {
result := make(map[string][]string, len(ids))
if len(ids) == 0 {
return result
}
args := make([]any, len(ids))
for i, id := range ids {
args[i] = id
}
query := fmt.Sprintf(
`SELECT mt.ID, t.Name FROM %s t JOIN %s mt ON t.ID = mt.TagID WHERE mt.ID IN (?%s) ORDER BY mt.ID, t.Name`,
tenant("Tags"), tenant("message_tags"), strings.Repeat(",?", len(ids)-1),
) // #nosec
rows, err := db.Query(query, args...)
if err != nil {
logger.Log().Errorf("[tags] %s", err.Error())
return result
}
defer func() { _ = rows.Close() }()
for rows.Next() {
var id, name string
if err := rows.Scan(&id, &name); err != nil {
logger.Log().Errorf("[tags] %s", err.Error())
return result
}
result[id] = append(result[id], name)
}
return result
}
// Get message tags from the database for a given database ID
// Used when parsing a raw email.
func getMessageTags(id string) []string {