Skip to main content

Module search

Module search 

Source
Expand description

Search-client capability trait and types.

A SearchClientPlugin is the runtime-side face of a search backend: issue queries, push documents into an index, delete documents by id. Backends fall into two families:

  1. Dedicated search engines (@bext/search-meili, @bext/search-typesense, @bext/search-elastic) — an external service holds the inverted index and handles ranking.
  2. SQL full-text (@bext/search-pg) — the existing Postgres instance runs to_tsvector / plainto_tsquery on a regular table. No new infrastructure, good enough for “I want search and I have one database” sites.

The trait stays sync to match the rest of bext-plugin-api. Backends that speak native async (the Meilisearch SDK, the Elasticsearch Rust client) either use their blocking sibling or own a small tokio runtime and call block_on — the same pattern @bext/auth-jwt’s JWKS fetcher and @bext/flags-openfeature use. Plugins cannot expose async across the sandbox boundary, so the host-facing shape is sync.

§Query shape is intentionally small

SearchQuery carries a text string, equality filters on attributes, a limit, and an offset. That covers the 80% case (autocomplete, search within category, keyword + facet) without leaking any vendor’s query DSL into the trait. Two escape hatches exist for richer needs:

  • A backend that wants a raw JSON query can accept it in text and document the shape — the trait does not parse text.
  • A backend can expose its own richer API behind SearchClientPlugin at construction time, then narrow down to the trait when called from capability-dispatching code.

The alternative — growing a rich shared query DSL — is the trap the architecture doc calls out: vendor-coupled shapes end up looking like whichever backend shipped first and never fit the next one cleanly.

§Document and hit payloads are JSON strings

Document::fields_json and SearchHit::source_json are plain JSON strings, not serde_json::Values. This matches the Session capability carrying session data, the Lifecycle capability carrying event payloads, and the Feature Flag capability carrying structured flag values. The reason is the same every time: the WASM / QuickJS / nsjail sandbox ABI is flatter when it only has to transport bytes, and the host-facing code pays one serde_json::from_str at the edge instead of shoving a fully-typed value across the boundary.

Structs§

Document
A single document to push into an index.
SearchHit
A single search hit returned by the backend.
SearchQuery
A query to issue against a named index.
SearchResults
Result of a single search call.

Enums§

SearchError
Typed error returned by every SearchClientPlugin method.

Traits§

SearchClientPlugin
A search backend.