Overview01
github.com/xraph/forge/extensions/search provides a unified full-text search interface backed by
Elasticsearch, Meilisearch, Typesense, or an in-memory implementation. It registers a SearchService
in the DI container that manages connection lifecycle and exposes index management, document CRUD,
search, suggest, and autocomplete operations.
What It Registers02
| Service | DI Key | Type |
| Search service | search | *SearchService (also satisfies Search) |
The service is managed by Vessel. Vessel calls Start (connects to the backend) and Stop (disconnects) automatically.
Quick Start03
package main
import (
"context"
"fmt"
"github.com/xraph/forge"
"github.com/xraph/forge/extensions/search"
)
func main() {
app := forge.NewApp(forge.AppConfig{Name: "my-app", Version: "1.0.0"})
// Register with defaults (in-memory backend)
app.RegisterExtension(search.NewExtension())
// Or connect to Meilisearch
app.RegisterExtension(search.NewExtension(
search.WithDriver("meilisearch"),
search.WithURL("http://localhost:7700"),
search.WithAPIKey("your-api-key"),
))
ctx := context.Background()
app.Start(ctx)
defer app.Stop(ctx)
s := search.MustGetSearch(app.Container())
// Create an index with a schema
s.CreateIndex(ctx, "products", search.IndexSchema{
Fields: []search.FieldSchema{
{Name: "title", Type: "text", Searchable: true, Sortable: true},
{Name: "price", Type: "float", Filterable: true, Sortable: true},
{Name: "category", Type: "keyword", Filterable: true, Faceted: true},
},
})
// Index a document
s.Index(ctx, "products", search.Document{
ID: "prod-1",
Fields: map[string]any{"title": "Wireless Mouse", "price": 29.99, "category": "electronics"},
})
// Search with filters and facets
results, _ := s.Search(ctx, search.SearchQuery{
Index: "products",
Query: "wireless",
Filters: []search.Filter{{Field: "price", Operator: "<=", Value: 50.0}},
Facets: []string{"category"},
Limit: 10,
})
for _, hit := range results.Hits {
fmt.Printf(" %s (score: %.2f)\n", hit.ID, hit.Score)
}
}Using Search in Your Services04
Inject *search.SearchService into your constructors for automatic DI resolution:
type ProductSearch struct {
search search.Search
logger forge.Logger
}
func NewProductSearch(s *search.SearchService, logger forge.Logger) *ProductSearch {
return &ProductSearch{search: s, logger: logger}
}
func (ps *ProductSearch) Find(ctx context.Context, query string, maxPrice float64) ([]search.Hit, error) {
results, err := ps.search.Search(ctx, search.SearchQuery{
Index: "products",
Query: query,
Filters: []search.Filter{{Field: "price", Operator: "<=", Value: maxPrice}},
Highlight: true,
Limit: 20,
})
if err != nil {
return nil, err
}
return results.Hits, nil
}Register with Vessel:
forge.ProvideConstructor(app.Container(), NewProductSearch)Key Concepts05
Drivers -- select
inmemory,elasticsearch,meilisearch, ortypesensevia config. The in-memory driver is fully functional and useful for development and testing.Index management -- create, delete, list, and inspect indexes with typed schemas including field definitions, analyzers, synonyms, and ranking rules.
Document operations -- index single or bulk documents, retrieve by ID, update, and delete.
Search -- query with filters, sorting, faceting, highlighting, fuzzy matching, field boosting, and score thresholds.
Suggest and Autocomplete -- dedicated query types for search-as-you-type experiences.
Important Runtime Notes06
Extension health delegates to the backend's
Ping()method via Vessel.The
Stop()method logs disconnect errors but does not propagate them to avoid blocking shutdown.Backend stubs for Elasticsearch, Meilisearch, and Typesense are declared. Verify your target driver is fully implemented before production use.