Skip to main content
Related: Configuration, Relation, Compiler, DX, Models, Observability Vector search lets you find documents by meaning rather than exact keywords. The gem wraps Typesense’s vector search capabilities with a Ruby DSL covering schema declarations, query composition, and automatic compiler behaviour.

Configuration

Set a default embedding model once in your initializer. Every embedding declaration that does not specify its own model inherits this value.
If no model is set globally or per-field, the embedding macro raises SearchEngine::Errors::ConfigurationError at load time.

Schema: the embedding macro

Declare embedding fields on your model with the embedding macro. It registers a :vector attribute internally and builds the Typesense embed block (or num_dim for external embeddings) at schema compile time.

Basic forms

Unindexed embed sources

If an embed source field is only needed for embedding (not for keyword search, filtering, or sorting), declare it with index: false. The schema compiler detects the from: reference and emits the field with Typesense-native "index": false instead of omitting it entirely. This avoids keyword-indexing overhead and the auto-generated *_blank hidden field.
The compiled schema will include brand_name as { "name": "brand_name", "type": "string", "index": false, "optional": true }. Typesense stores the value on disk and feeds it to the embedding model, but it consumes no in-memory index space.

External embeddings

When your application generates vectors outside Typesense, declare num_dim: without from:. The mapper expects your map block to supply a float array of the declared dimension.
No embed block is emitted in the schema; Typesense stores the field as float[] with the given dimension constraint.

Name resolution rules

Model resolution precedence: per-field model: > config.embedding.model > raises ConfigurationError. from: inference: when from: is omitted and the field is not external, the macro infers from: [bare_name] where bare_name is the positional argument before the _embedding suffix.

Macro signature

When the query string is not "*" and no explicit query: vector is provided, Typesense auto-embeds the query text with the same model used at index time and performs nearest-neighbor search.
The compiler auto-appends product_embedding to query_by so Typesense performs rank fusion between keyword and vector results.

Field auto-resolution

When a model declares exactly one embedding, the field argument can be omitted entirely. The gem resolves it automatically:
This makes vector_search act as a behaviour modifier — just chain it to enable semantic/hybrid mode.
Auto-resolution raises InvalidVectorQuery when the model has zero or multiple embeddings. In those cases, pass the field explicitly.
When k: is omitted, Typesense applies its server default (10). You only need to specify k: when you want a different value. Combine keyword and vector results with an explicit alpha weight. Typesense uses rank fusion:
Start with alpha: 0.7 and tune based on your dataset. Higher alpha favours semantic similarity; lower alpha favours exact keyword matches.

Find similar documents

find_similar is sugar over vector_search with id:. Typesense retrieves the stored embedding for the given document and finds its nearest neighbors.
When the model has a single embedding, field: can be omitted:
Chain filters to refine results:

Historical queries

Weight multiple past queries and let Typesense blend their embeddings server-side.
weights: must sum to approximately 1.0 (tolerance: 0.01). The gem validates this and raises InvalidVectorQuery if violated.

External vector queries

When you generate embeddings in your own pipeline, pass the float array directly.
This bypasses Typesense’s auto-embedding; the provided vector is used as-is for nearest-neighbor search.

Sort by vector distance

Use order(vector_distance: :asc) as a secondary sort to rank keyword results by their semantic proximity.
The compiler resolves vector_distance to the Typesense token _vector_query(product_embedding:([])):asc.
order(vector_distance: ...) requires .vector_search to be chained on the same relation. Using it without vector search raises InvalidVectorQuery.

Distance threshold

Cap results by cosine distance to filter out low-relevance matches.

HNSW tuning

Override HNSW search parameters per query.
For small result sets, bypass HNSW with brute-force flat search:

Auto-exclude behaviour

Embedding fields contain large float arrays (384—1536 dimensions). To avoid inflating response payloads, the compiler automatically adds the embedding field to exclude_fields unless you explicitly select it. To include the raw vectors in the response:
Vector search works inside multi_search blocks:

Compiler mapping

The compiler:
  1. Builds the vector_query string: product_embedding:([], k:200, alpha:0.8)
  2. Auto-appends the embedding field to query_by in hybrid mode (text query + vector search, no explicit query: array)
  3. Auto-adds the embedding field to exclude_fields unless explicitly selected
  4. Resolves order(vector_distance: ...) to the real Typesense sort token

DX & explain

All DX helpers include vector search state. Raw float arrays are redacted to [<N dims>] in all surfaces.

Method signatures

Mutually exclusive modes: query:, id:, and queries:. Providing more than one raises InvalidVectorQuery. Last vector_search call wins (Typesense supports one vector_query per search).

find_similar

Sugar over vector_search(field, id: document_id, ...). When field: is omitted, the sole embedding on the model is used automatically (same resolution as vector_search).

Observability

The compiler emits a search_engine.vector.compile event with: Compact logging redacts raw vector arrays. OTel spans include these attributes when enabled. See Observability for event payloads and log format.