package search import ( "context" "tercul/internal/domain" "tercul/internal/platform/search" ) // BleveSearchService provides keyword and exact-match search capabilities // Complements Weaviate's vector/semantic search with traditional full-text search type BleveSearchService interface { IndexTranslation(ctx context.Context, translation domain.Translation) error IndexAllTranslations(ctx context.Context) error SearchTranslations(ctx context.Context, query string, filters map[string]string, limit int) ([]TranslationSearchResult, error) } type TranslationSearchResult struct { ID uint Score float64 Title string Content string Language string TranslatableID uint TranslatableType string } type bleveSearchService struct { translationRepo domain.TranslationRepository } func NewBleveSearchService(translationRepo domain.TranslationRepository) BleveSearchService { return &bleveSearchService{ translationRepo: translationRepo, } } func (s *bleveSearchService) IndexTranslation(ctx context.Context, translation domain.Translation) error { return search.BleveClient.AddTranslation(translation) } func (s *bleveSearchService) IndexAllTranslations(ctx context.Context) error { // Get all translations from the database and index them translations, err := s.translationRepo.ListAll(ctx) if err != nil { return err } for _, translation := range translations { if err := search.BleveClient.AddTranslation(translation); err != nil { return err } } return nil } func (s *bleveSearchService) SearchTranslations(ctx context.Context, query string, filters map[string]string, limit int) ([]TranslationSearchResult, error) { results, err := search.BleveClient.Search(query, filters, limit) if err != nil { return nil, err } var searchResults []TranslationSearchResult for _, hit := range results.Hits { // Extract fields from the hit fields := hit.Fields result := TranslationSearchResult{ Score: hit.Score, } // Safely extract fields with type assertions if id, ok := fields["id"].(float64); ok { result.ID = uint(id) } if title, ok := fields["title"].(string); ok { result.Title = title } if content, ok := fields["content"].(string); ok { result.Content = content } if language, ok := fields["language"].(string); ok { result.Language = language } if translatableID, ok := fields["translatable_id"].(float64); ok { result.TranslatableID = uint(translatableID) } if translatableType, ok := fields["translatable_type"].(string); ok { result.TranslatableType = translatableType } searchResults = append(searchResults, result) } return searchResults, nil }