Searchers Service Kind

Overview

Searchers are entity-specific services responsible for providing search functionality across domain models. They create searchable indices, handle search queries with filtering and scoping, and generate search result presentations for UI components.

Architecture

Core Components

Base Searcher Library (src/lib/searcher/) - Searcher struct: Core search engine with indexing and query capabilities - SearchValue: Individual search result item with metadata - ScopeItem: Scope-based filtering mechanism - Mod: Search modification object for query parameters

Entity-Specific Searchers (src/srv/searchers/) - Each domain model has its own dedicated searcher (e.g., GardenSearcher, UserSearcher) - All searchers follow the same interface pattern and method conventions - Integrated with Fetchers, Stringers, Presenters, and Urlers

Key Structures

SearchValue

Represents a searchable entity with metadata: - Id: Unique search identifier (prefixed with model type) - Text: Searchable text content (lowercased) - ModelId: Original entity ID - Model: Entity type identifier - Url: Navigation URL for the entity - Scope: Array of scope items for filtered search - Index: Position in search results

ScopeItem

Enables scoped/filtered searching: - Name: Scope category name - Value: Scope filter value - Text: Human-readable scope description

Standard Methods

Every entity searcher implements these standard methods:

Search Index Methods

SearchValues(ctx) []searcher.SearchValue
  • Generate searchable values for all entities of this type
  • Convert entities to searchable format with URLs and text
  • Include scope information for filtered searching

Search Result Methods

GetSearchItem(ctx, value) goc.HTML
  • Generate HTML presentation for a search result
  • Load entity data and format for display
  • Use presenters to create consistent search result UI

Internal Methods

getScope(ctx, entity) []*searcher.ScopeItem
  • Generate scope items for entity-specific filtering
  • Enable contextual search within entity relationships
  • Return empty slice if no scoping is needed

Usage Examples

Search Index Generation

// Generate search values for gardens
values := gardenSearcher.SearchValues(ctx)
// Returns SearchValue objects with searchable text and metadata

Search Result Presentation

// Get HTML for specific search result
searchValue := searcher.SearchValue{ModelId: "123", Model: "garden"}
html := gardenSearcher.GetSearchItem(ctx, searchValue)

Full Search Workflow

// Index all searchable content
indexValues := searchers.Indexing(ctx)  // Aggregates all entity searchers

// Perform search with query
results := searcher.SearchValues(ctx, "text", "garden search", nil)

// Generate HTML for results
items := searchers.GetSearchItems(ctx, results)

Search Indexing Process

1. Entity Retrieval

entities := this.GardenFetcher.MustFindAll(ctx, nil)

2. Search Value Creation

for _, entity := range entities {
    value := searcher.SearchValue{
        Id:      "garden_" + entity.Id,
        Text:    strings.ToLower(this.GardenStringer.MustToString(entity)),
        ModelId: entity.Id,
        Model:   "garden",
        Url:     this.AppUrler.ShowGarden(entity.Id),
        Scope:   this.getScope(ctx, entity),
    }
    values = append(values, value)
}

Search Query Processing

The base Searcher supports various query modifiers:

":garden my search text"  // Search only within garden entities

Text Matching

  • Case-insensitive substring matching
  • Supports multi-word queries
  • Results limited to 10 items by default
  • Filter results based on scope items
  • Enable contextual search within relationships
  • Support hierarchical filtering

Integration with Framework

With Fetchers

entities := this.GardenFetcher.MustFindAll(ctx, nil)
entity := this.GardenFetcher.ShallFindOneById(ctx, id, nil)

With Stringers

Text: strings.ToLower(this.GardenStringer.MustToString(entity))

With Presenters

return this.GardenPresenter.Search(ctx, &value, entity)

With Urlers

Url: this.AppUrler.ShowGarden(entity.Id)

Global Search Coordination

The Searchers struct coordinates search across all entities:

Index Aggregation

func (this *Searchers) Indexing(ctx context.Context) []searcher.SearchValue {
    var values []searcher.SearchValue
    values = append(values, this.GardenSearcher.SearchValues(ctx)...)
    values = append(values, this.UserSearcher.SearchValues(ctx)...)
    // ... all other searchers
    return values
}

Result Presentation

func (this *Searchers) GetSearchItem(ctx context.Context, value searcher.SearchValue) goc.HTML {
    switch value.Model {
    case "garden":
        return this.GardenSearcher.GetSearchItem(ctx, value)
    case "user":
        return this.UserSearcher.GetSearchItem(ctx, value)
    }
}

Search Performance

Indexing Strategy

  • In-memory search index for fast queries
  • Lazy loading with indexing state management
  • Background indexing to prevent blocking

Query Optimization

  • Result limiting (10 items max by default)
  • Early termination on match limits
  • Efficient string matching with case normalization

Search Features

Query Syntax

  • Plain text: "garden name" - Basic text search
  • Model filter: ":garden vegetable" - Search within specific entity type
  • Multi-word: "my garden vegetables" - Match all words

Result Metadata

  • Direct navigation URLs for each result
  • Entity type identification for UI styling
  • Scope information for contextual filtering
  • Consistent HTML presentation across entity types