Federated multi-search
Overview
Federate multiple labeledRelations into a single Typesense multi-search request while preserving order and mapping results back to labels.
- Pure builder: collects labeled relations, no HTTP
- Order preserved: results map back in insertion order
- Unique labels: labels are case-insensitive and must be unique
- Common params:
common:is shallow-merged into each per-search payload; per-search keys win - No URL knobs in body: cache options are handled as URL/common params by the client
DSL
Label rules
- Accepts
StringorSymbol - Canonicalization:
label.to_s.downcase.to_sym - Must be unique (case-insensitive)
Common params merge
- Merge precedence: per-search params override
common:keys - URL-only keys filtered from bodies:
use_cache,cache_ttl(these live in URL opts)
Guardrails
- Unique labels: accept
StringorSymbol; canonicalization islabel.to_s.downcase.to_symand labels must be unique (case-insensitive). - No URL-only knobs in bodies:
use_cache,cache_ttlare URL opts only and are filtered from bothcommon:and per-search bodies. - Actionable errors: duplicate labels, invalid relation (missing bound collection), or exceeding
SearchEngine.config.multi_search_limitraiseArgumentErrorbefore any network call. - Per-search api_key: unsupported in Typesense multi-search; set
SearchEngine.config.api_keyinstead.
Mapping (Relation → per-search payload)
| Relation aspect | Per-search key |
|---|---|
query (q, default *) | q |
| fields to search | query_by |
filters (AST / where) | filter_by |
order (order) | sort_by |
select (select) | include_fields |
pagination (page/per) | page, per_page |
| infix (config or override) | infix |
Compile flow
Per-search API key policy
Per-searchapi_key is not supported by the underlying Typesense multi-search API. Passing a non-nil api_key to m.add raises an ArgumentError. Use the global SearchEngine.config.api_key instead.
Result mapping
The helper pairs Typesense responses back to the original labels and model classes, returning aSearchEngine::Multi::ResultSet by default. For a dedicated wrapper with additional Hash-like APIs, see MultiResult below.
#[]/#dig(label)→SearchEngine::Result#labels→[:label_a, :label_b, …]in insertion order#to_h→{ label: Result, ... }#each_pair→ iterate(label, result)in order
MultiResult
A lightweight, ordered wrapper over the raw multi-search list that exposes labeledResult objects and stable insertion order. Hydration uses:
- The model class captured alongside each label at request time
- Fallback to the collection registry when the raw item exposes a
collection - Fallback to
OpenStructotherwise
- Accessors:
#[],#dig,#labels,#keys,#to_h,#each_label - Label canonicalization:
label.to_s.downcase.to_sym - Order: deterministic and identical to the order of
m.add
URL opts, caching, and limits
- URL-level caching knobs are passed as URL options only and never included in per-search bodies.
url_optsare built from config:{ use_cache: SearchEngine.config.use_cache, cache_ttl: SearchEngine.config.cache_ttl_s }.- A hard cap on the number of searches is enforced via
SearchEngine.config.multi_search_limit(default: 50). Exceeding this limit raises before any network call.
Raw response helper
If you prefer the raw response returned by the Typesense client, use:SearchEngine::Errors and, when available, the first failing label and status are included in the error message.
Memoization & Ergonomics
- Single roundtrip: the HTTP request is performed exactly once by
Client#multi_search. - The raw response array is stored privately inside
MultiResultand hydration into{ label => Result }occurs once. - All accessors and helpers operate purely in-memory and never perform HTTP.
#to_hreturns a shallow copy of the mapping; insertion order preserved#each_labelyields(label, result)in order; returns Enumerator without a block#map_labelsis a convenience implemented viaeach_labeland is equally pure
See also
- Client for URL/common params and error mapping
- Relation Guide for query composition and compilation
- Configuration for cache knobs and
multi_search_limit
Observability
Observability · DX · Testing Multi-search emits a single event around the network call:- Event:
search_engine.multi_search - Payload:
{ searches_count, labels, http_status, source: :multi } - Duration: available as
ev.durationfor subscribers
q, or filter_by. Labels are considered safe.
Example compact log line shape: