package search import ( "strings" "gorm.io/gorm" "senlinai-agent/backend/internal/domain" ) type Service struct { db *gorm.DB } type Result struct { Type string `json:"type"` ID uint `json:"id"` ProjectID uint `json:"project_id"` Title string `json:"title"` Snippet string `json:"snippet"` } func NewService(database *gorm.DB) *Service { return &Service{db: database} } func (s *Service) Search(userID uint, query string) ([]Result, error) { query = strings.TrimSpace(query) if query == "" { return []Result{}, nil } if s.db.Dialector.Name() == "postgres" { return s.searchPostgres(userID, query) } var notes []domain.Note if err := s.db.Joins("JOIN projects ON projects.id = notes.project_id"). Where("projects.owner_id = ? AND (notes.title LIKE ? OR notes.markdown LIKE ?)", userID, "%"+query+"%", "%"+query+"%"). Find(¬es).Error; err != nil { return nil, err } results := make([]Result, 0, len(notes)) for _, note := range notes { results = append(results, Result{Type: "note", ID: note.ID, ProjectID: note.ProjectID, Title: note.Title, Snippet: note.Markdown}) } return results, nil } func (s *Service) searchPostgres(userID uint, query string) ([]Result, error) { var results []Result err := s.db.Raw(` SELECT 'note' AS type, notes.id, notes.project_id, notes.title, notes.markdown AS snippet FROM notes JOIN projects ON projects.id = notes.project_id WHERE projects.owner_id = ? AND to_tsvector('simple', coalesce(notes.title, '') || ' ' || coalesce(notes.markdown, '')) @@ plainto_tsquery('simple', ?) ORDER BY notes.updated_at DESC LIMIT 50 `, userID, query).Scan(&results).Error return results, err }