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:
Model-Specific Search
":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
Scoped Search
- 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