Skip to main content
Concise reference for running multiple labeled searches in a single Typesense round‑trip, handling results, and applying safe URL‑level caching.
  • What it is: federates multiple Relations into one request; results are mapped back to labels in order.
  • When to use: homepage modules, typeahead+categories, federated search blocks; prefer it over multiple sequential calls to reduce latency.
  • When not to use: completely independent pages with different lifecycles or when strong isolation/errors per request are critical.

Builder DSL

Use SearchEngine.multi_search(common: …) { |m| m.add(label, relation) }. The common: hash is shallow‑merged into each compiled relation payload; per‑search values from the relation win on key conflicts. Required verbatim example:
Minimal variations:
  • Override pagination per search while inheriting common::
  • Add field selection per search:
See example controller: examples/demo_shop/app/controllers/search_controller.rb.

Per‑search overrides

Per‑relation chainers and options(…) override or augment common: on a per‑entry basis:
  • where/filters → filter_by
  • ordersort_by
  • select/excludeinclude_fields/exclude_fields
  • page/perpage/per_page
  • options(q: …, query_by: …, infix: …) map into the compiled body
Example (per‑search query_by and filters override common:):
Guardrails:
  • Consistent query_by per collection: ensure each collection’s fields exist; unknown fields raise during compile when strict field checks are enabled.
  • URL‑only knobs: use_cache, cache_ttl live at the URL level and are filtered from both common: and per‑search bodies.

Result handling with MultiResult

SearchEngine.multi_search returns SearchEngine::Multi::ResultSet (hash‑like). If you prefer a dedicated wrapper, use SearchEngine.multi_search_result which returns SearchEngine::MultiResult.
  • Hash‑like access: res[:books] (matches the labels you added)
  • Each entry is a SearchEngine::Result with #to_a, #found, #empty?, #raw, etc.
  • Order is preserved; labels are case‑insensitive symbols internally.
From the snippet above, res[:books].to_a returns hydrated hits for the :books entry. Example with MultiResult directly:
Partial failures: if Typesense returns per‑entry error statuses inside a 200 response, inspect entry.raw to branch UI gracefully (avoid raising globally):

Controller usage patterns (Rails)

Keep controllers thin; build relations with only request‑dependent inputs and pass a single multi‑result to the view.
Caching notes:
  • Prefer URL/request‑level cache keys derived from stable inputs (e.g., params.slice(:q, :page, :per, :filters)).
  • Multi‑search uses URL‑level cache knobs from config: { use_cache: SearchEngine.config.use_cache, cache_ttl: SearchEngine.config.cache_ttl_s }.
  • Per‑relation options(use_cache:, cache_ttl:) are not applied in the multi‑search path; set them via config for multi‑search.
  • Consider setting HTTP cache headers at the controller/edge layer based on those inputs; avoid embedding secrets.

Compile flow

DX & debugging

Prefer network‑safe introspection for demos and debugging:
  • See the DX page for details and redaction policy.
  • For fully offline tests/examples, use the stub client approach in the Testing page.

Presets & Curation in multi‑search

Applied per relation. Each m.add carries its own preset/curation context and compiles independently.
  • Presets: per‑search preset and preset_mode (merge/only/lock) are honored during compile.
  • Curation: pinned_hits, hidden_hits, curation_tags, filter_curated_hits are emitted body‑only when present.
See the dedicated Presets and Curation docs for details and caveats.

Edge cases & troubleshooting

  • Misaligned query_by: ensure each collection’s fields exist; validate with rel.explain and rel.dry_run!.
  • Unknown fields: selection and filtering validate against declared attributes; fix names or disable strictness per environment.
  • Mixed grouping: grouping options are compiled per relation; UI should handle grouped vs non‑grouped results independently.
  • Differing per/page: expected; each box paginates independently.
  • Partial failures: the helper augments raised API errors with failing label when the HTTP status is non‑2xx; for 2xx with per‑entry errors, inspect result.raw per label and degrade gracefully.
  • Redaction: never print API keys or raw filter_by directly; use dry_run!, to_curl, or explain which apply redaction.