1
0
mirror of https://github.com/axllent/mailpit.git synced 2026-05-20 10:10:32 +02:00

Chore: Enhance SetMessageTags function to improve tag handling and batch deletions

This commit is contained in:
Ralph Slooten
2026-05-09 16:35:21 +12:00
parent f11fc1ffe0
commit e4c3442e39
+59 -21
View File
@@ -24,40 +24,58 @@ var (
// SetMessageTags will set the tags for a given database ID, removing any not in the array
func SetMessageTags(id string, tags []string) ([]string, error) {
// Clean and deduplicate incoming tags (case-insensitive)
seen := make(map[string]struct{})
applyTags := []string{}
for _, t := range tags {
t = tools.CleanTag(t)
if t != "" && config.ValidTagRegexp.MatchString(t) && !tools.InArray(t, applyTags) {
applyTags = append(applyTags, t)
}
}
tagNames := []string{}
currentTags := getMessageTags(id)
origTagCount := len(currentTags)
for _, t := range applyTags {
if t == "" || !config.ValidTagRegexp.MatchString(t) || tools.InArray(t, currentTags) {
if t == "" || !config.ValidTagRegexp.MatchString(t) {
continue
}
lc := strings.ToLower(t)
if _, exists := seen[lc]; exists {
continue
}
seen[lc] = struct{}{}
applyTags = append(applyTags, t)
}
// Fetch existing tags once and index by lowercase name for O(1) lookup
currentTags := getMessageTags(id)
currentSet := make(map[string]struct{}, len(currentTags))
for _, t := range currentTags {
currentSet[strings.ToLower(t)] = struct{}{}
}
// Build apply set for O(1) lookup when computing deletions
applySet := make(map[string]struct{}, len(applyTags))
for _, t := range applyTags {
applySet[strings.ToLower(t)] = struct{}{}
}
// Add tags not already on the message
tagNames := []string{}
for _, t := range applyTags {
if _, exists := currentSet[strings.ToLower(t)]; exists {
continue
}
name, err := addMessageTag(id, t)
if err != nil {
return []string{}, err
}
tagNames = append(tagNames, name)
}
if origTagCount > 0 {
currentTags = getMessageTags(id)
for _, t := range currentTags {
if !tools.InArray(t, applyTags) {
if err := deleteMessageTag(id, t); err != nil {
return []string{}, err
}
}
// Delete tags removed from the message in a single batch query
toDelete := []string{}
for _, t := range currentTags {
if _, exists := applySet[strings.ToLower(t)]; !exists {
toDelete = append(toDelete, t)
}
}
if len(toDelete) > 0 {
if err := deleteMessageTags(id, toDelete); err != nil {
return []string{}, err
}
}
@@ -126,6 +144,26 @@ func addMessageTag(id, name string) (string, error) {
return addMessageTag(id, name)
}
// deleteMessageTags deletes multiple tags from a message in a single query
func deleteMessageTags(id string, names []string) error {
args := make([]any, 1+len(names))
args[0] = id
for i, n := range names {
args[i+1] = n
}
query := fmt.Sprintf(
`DELETE FROM %s WHERE ID = ? AND TagID IN (SELECT ID FROM %s WHERE Name IN (?%s))`,
tenant("message_tags"), tenant("tags"), strings.Repeat(",?", len(names)-1),
) // #nosec
if _, err := db.Exec(query, args...); err != nil {
return err
}
return pruneUnusedTags()
}
// DeleteMessageTag deletes a tag from a message
func deleteMessageTag(id, name string) error {
if _, err := sqlf.DeleteFrom(tenant("message_tags")).