feat: migrate tags and contributors from TEXT to TEXT[] arrays
Convert tags and contributors columns from comma-separated strings to PostgreSQL TEXT[] arrays for better data normalization and query performance. Database Changes: - schema.sql: Change tags/contributors from TEXT to TEXT[] - schema.sql: Add GIN indexes for fast array searches - queries.sql: Update search queries to use ANY() operator - queries.sql: Update fuzzy search with unnest() for arrays Generated Code (sqlc): - models.go: Auto-generated with []string types for tags/contributors - queries.sql.go: Auto-generated with proper array handling Handler Changes: - media.go: Update request structs to use []string for tags/contributors - media.go: Remove pgtype.Text wrapping, use direct array assignment - media.go: Add tag normalization in CreateMediaItemHandler - collections.go: Update tags evaluation to join arrays for comparison - collections.go: Add strings import for Join() function Service Changes: - ebook_scanner.go: Update EbookMetadata struct to use []string - ebook_scanner.go: Remove string Join(), assign arrays directly - collection_service.go: Update tags rule evaluation to join arrays - collection_service.go: Add strings import New Utilities: - internal/utils/tags.go: Create NormalizeTags(), JoinTags(), SplitTags() - Normalizes tags by trimming, lowercasing, removing duplicates/empties API Documentation: - bruno/media-items/Create Media Item.bru: Update examples to use arrays - bruno/media-items/Update Media Item.bru: Update examples to use arrays - Update docs: tags/contributors now array of string Breaking Change: - JSON format changes from "tags": "tag1,tag2" to "tags": ["tag1", "tag2"] - Tests already use array format (no changes needed) Benefits: - GIN indexes enable faster array searches - Normalization prevents data quality issues (case, duplicates) - Array operations use PostgreSQL native operators (ANY, &&, unnest) - Better separation of concerns (no string parsing in application)
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -264,8 +265,9 @@ func (s *CollectionService) EvaluateRules(mediaItem database.ListMediaItemsRow,
|
||||
eval.Matches = s.evaluateRule(yearStr, rule.Operator, rule.Value)
|
||||
}
|
||||
case "tags":
|
||||
if mediaItem.Tags.Valid {
|
||||
eval.Matches = s.evaluateRule(mediaItem.Tags.String, rule.Operator, rule.Value)
|
||||
if len(mediaItem.Tags) > 0 {
|
||||
tagsStr := strings.Join(mediaItem.Tags, ", ")
|
||||
eval.Matches = s.evaluateRule(tagsStr, rule.Operator, rule.Value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,11 +41,11 @@ type EbookMetadata struct {
|
||||
SeriesNumber int32
|
||||
Publisher string
|
||||
PublishDate time.Time
|
||||
Contributors string
|
||||
Contributors []string
|
||||
CoverPath string
|
||||
ISBN string
|
||||
ASIN string
|
||||
Tags string
|
||||
Tags []string
|
||||
|
||||
Phase1HashInfo *HashInfo
|
||||
Phase1FormatFormats []*FormatInfo
|
||||
@@ -468,8 +468,8 @@ func (s *EbookScanner) processEbookFile(ctx context.Context, path string) error
|
||||
SeriesNumber: pgtype.Int4{Int32: metadata.SeriesNumber, Valid: metadata.SeriesNumber > 0},
|
||||
Publisher: pgtype.Text{String: metadata.Publisher, Valid: metadata.Publisher != ""},
|
||||
DatePublished: pgtype.Date{Time: metadata.PublishDate, Valid: !metadata.PublishDate.IsZero()},
|
||||
Contributors: pgtype.Text{String: metadata.Contributors, Valid: metadata.Contributors != ""},
|
||||
Tags: pgtype.Text{String: metadata.Tags, Valid: metadata.Tags != ""},
|
||||
Contributors: metadata.Contributors,
|
||||
Tags: metadata.Tags,
|
||||
AddedByAdminID: s.adminID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -576,7 +576,8 @@ func (s *EbookScanner) extractEPUBMetadata(path string) (*EbookMetadata, error)
|
||||
|
||||
// Contributors
|
||||
if contributors, err := book.MetadataByKey("contributor"); err == nil && len(contributors) > 0 {
|
||||
metadata.Contributors = strings.Join(contributors, ", ")
|
||||
// Already a []string from xml parsing, just assign
|
||||
metadata.Contributors = contributors
|
||||
}
|
||||
|
||||
// ISBN
|
||||
@@ -602,7 +603,8 @@ func (s *EbookScanner) extractEPUBMetadata(path string) (*EbookMetadata, error)
|
||||
|
||||
// Tags
|
||||
if tags, err := book.MetadataByKey("subject"); err == nil && len(tags) > 0 {
|
||||
metadata.Tags = strings.Join(tags, ", ")
|
||||
// Already a []string from xml parsing, just assign
|
||||
metadata.Tags = tags
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
|
||||
Reference in New Issue
Block a user