Files
poimen-workflows/internal/search/workflow_search.go
T

235 lines
4.9 KiB
Go
Raw Normal View History

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)
}