package search import ( "fmt" "regexp" "strings" "sync" "time" ) // WorkflowEntry represents an indexed workflow type WorkflowEntry struct { ID string Name string Status string CreatedAt time.Time UpdatedAt time.Time Tags []string Content string Assignee string } // WorkflowSearch provides full-text search and filtering type WorkflowSearch struct { mu sync.RWMutex entries map[string]*WorkflowEntry index map[string][]string // word -> workflow IDs filters map[string]interface{} } // NewWorkflowSearch creates a new workflow search index func NewWorkflowSearch() *WorkflowSearch { return &WorkflowSearch{ entries: make(map[string]*WorkflowEntry), index: make(map[string][]string), filters: make(map[string]interface{}), } } // Index adds a workflow to the search index func (ws *WorkflowSearch) Index(entry *WorkflowEntry) error { if entry.ID == "" { return fmt.Errorf("workflow ID required") } ws.mu.Lock() defer ws.mu.Unlock() ws.entries[entry.ID] = entry // Index content words := strings.Fields(strings.ToLower(entry.Content + " " + entry.Name)) for _, word := range words { // Remove punctuation clean := strings.Trim(word, ".,!?;:") if clean != "" { ws.index[clean] = append(ws.index[clean], entry.ID) } } return nil } // Search performs full-text search func (ws *WorkflowSearch) Search(query string) []*WorkflowEntry { ws.mu.RLock() defer ws.mu.RUnlock() query = strings.ToLower(query) matches := make(map[string]int) words := strings.Fields(query) for _, word := range words { if ids, exists := ws.index[word]; exists { for _, id := range ids { matches[id]++ } } } // Sort by match count result := make([]*WorkflowEntry, 0) for id := range matches { if entry, exists := ws.entries[id]; exists { result = append(result, entry) } } return result } // FilterByStatus filters workflows by status func (ws *WorkflowSearch) FilterByStatus(status string) []*WorkflowEntry { ws.mu.RLock() defer ws.mu.RUnlock() result := make([]*WorkflowEntry, 0) for _, entry := range ws.entries { if entry.Status == status { result = append(result, entry) } } return result } // FilterByAssignee filters workflows by assignee func (ws *WorkflowSearch) FilterByAssignee(assignee string) []*WorkflowEntry { ws.mu.RLock() defer ws.mu.RUnlock() result := make([]*WorkflowEntry, 0) for _, entry := range ws.entries { if entry.Assignee == assignee { result = append(result, entry) } } return result } // FilterByTag filters workflows by tag func (ws *WorkflowSearch) FilterByTag(tag string) []*WorkflowEntry { ws.mu.RLock() defer ws.mu.RUnlock() result := make([]*WorkflowEntry, 0) for _, entry := range ws.entries { for _, t := range entry.Tags { if t == tag { result = append(result, entry) break } } } return result } // FilterByDateRange filters workflows by date range func (ws *WorkflowSearch) FilterByDateRange(start, end time.Time) []*WorkflowEntry { ws.mu.RLock() defer ws.mu.RUnlock() result := make([]*WorkflowEntry, 0) for _, entry := range ws.entries { if entry.CreatedAt.After(start) && entry.CreatedAt.Before(end) { result = append(result, entry) } } return result } // SearchRegex performs regex search on content func (ws *WorkflowSearch) SearchRegex(pattern string) ([]*WorkflowEntry, error) { re, err := regexp.Compile(pattern) if err != nil { return nil, err } ws.mu.RLock() defer ws.mu.RUnlock() result := make([]*WorkflowEntry, 0) for _, entry := range ws.entries { if re.MatchString(entry.Content) || re.MatchString(entry.Name) { result = append(result, entry) } } return result, nil } // SaveFilter saves a named filter func (ws *WorkflowSearch) SaveFilter(name string, filter interface{}) { ws.mu.Lock() defer ws.mu.Unlock() ws.filters[name] = filter } // GetFilter retrieves a saved filter func (ws *WorkflowSearch) GetFilter(name string) (interface{}, bool) { ws.mu.RLock() defer ws.mu.RUnlock() filter, exists := ws.filters[name] return filter, exists } // GetAll returns all workflows func (ws *WorkflowSearch) GetAll() []*WorkflowEntry { ws.mu.RLock() defer ws.mu.RUnlock() result := make([]*WorkflowEntry, 0, len(ws.entries)) for _, entry := range ws.entries { result = append(result, entry) } return result } // GetByID retrieves a workflow by ID func (ws *WorkflowSearch) GetByID(id string) (*WorkflowEntry, bool) { ws.mu.RLock() defer ws.mu.RUnlock() entry, exists := ws.entries[id] return entry, exists } // Delete removes a workflow from the index func (ws *WorkflowSearch) Delete(id string) error { ws.mu.Lock() defer ws.mu.Unlock() if _, exists := ws.entries[id]; !exists { return fmt.Errorf("workflow not found: %s", id) } delete(ws.entries, id) return nil } // Clear clears the entire index func (ws *WorkflowSearch) Clear() { ws.mu.Lock() defer ws.mu.Unlock() ws.entries = make(map[string]*WorkflowEntry) ws.index = make(map[string][]string) }