Overview
Presets are server-side bundles of query options that you can attach to a relation to enforce consistent filters, sorts, or selections. They provide a global config (optional namespace and enablement), per‑collection defaults, and a per‑relationpreset(name, mode:) DSL.
- Why: enforce consistent defaults, reuse across surfaces, and keep chains concise.
- Where: global config under
SearchEngine.config.presets; model-leveldefault_preset; relation-levelRelation#preset.
Config & Default preset
Presets are namespaced when enabled. The effective name is computed as”#_#” when both enabled and namespace are set; otherwise the bare token is used.
- Global config:
enabled,namespace, andlocked_domains(used by:lockmode) - Model default: declare
default_preset :tokenon aSearchEngine::Basesubclass - Reader:
YourModel.default_preset_namereturns the effective name (ornilif none)
- Namespacing is ignored when
enabledis false; tokens remain usable as declared. locked_domainsis normalized to Symbols and used by the compiler to prune keys in:lockmode.
Relation DSL
Apply a preset with copy‑on‑write semantics, validate inputs, and keep chaining ergonomics:Relation#preset(name, mode: :merge)validates the name and mode- Readers:
rel.preset_nameandrel.preset_mode(defaults to:merge) inspect/explainshow the preset and, for:lock, any dropped keys- Presets affect the compiler stage only; no global config is mutated
Strategies (:merge, :only, :lock)
| Mode | What is sent | Who wins on overlaps | Conflicts recorded |
|---|---|---|---|
| merge | preset + all chain params | chain | no |
| only | preset + essentials (q, page, per_page) | n/a (others dropped) | no |
| lock | preset + chain minus locked domains | preset | yes (dropped keys) |
- merge: Send preset and all compiled params. When Typesense sees overlapping knobs, the later param wins; here the chain wins. No conflicts are recorded.
- only: Keep only essentials from the chain (
q,page,per_page) and send the preset. Use for strict server‑defined queries with local pagination. - lock: Drop chain keys that belong to
SearchEngine.config.presets.locked_domains(default:filter_by,sort_by,include_fields,exclude_fields), record dropped keys, and include them inexplain.
Mermaid — Strategy flow
Compiler mapping & pruning
The compiler injectspreset: <effective_name> into the params and prunes keys according to the chosen strategy.
- only: keep
q,page,per_page; drop the rest - lock: drop keys in
SearchEngine.config.presets.locked_domainsand record them - merge: no pruning
| Relation state | Compiler output |
|---|---|
preset_name, preset_mode | preset, _preset_mode |
| state → params | q, filter_by, sort_by, include_fields, page, per_page, … |
| pruning (only) | keep essentials only |
| pruning (lock) | drop locked_domains keys |
Mermaid — State → params mapping
Example (reused):Conflicts
Conflicts are detected only inmode: :lock and occur when a compiled chain key belongs to SearchEngine.config.presets.locked_domains. Such keys are dropped from the final params and recorded.
- Accessor:
Relation#preset_conflicts→[{ key: :filter_by, reason: :locked_by_preset }, ...](deterministic, frozen) - Explain: lists the effective preset and a line per dropped key with a humanized reason
- Inspect: appends a compact token:
preset=prod_brand_curated(mode=lock; conflicts=filter_by,sort_by) - Redaction: keys only; no raw values
Mermaid — Conflict resolution
Multi‑search
Presets are applied per search entry. Eachsearches[] item carries its own preset and respects that relation’s mode.
:merge: pass through compiled params (includespreset):only: keep onlycollection,q,page,per_page, andpreset:lock: drop keys listed inSearchEngine.config.presets.locked_domains
Observability & troubleshooting
Events (keys and counts only; values redacted elsewhere):search_engine.preset.apply— emitted once per compile when a preset is present- Payload:
preset_name,mode,locked_domains,pruned_keys
- Payload:
search_engine.preset.conflict— emitted once per compile in:lockmode when keys were dropped- Payload:
keys,count,mode,preset_name
- Payload:
- Text: appends
pz=<name>|m=<mode>|pk=<count>|ld=<count>; whenpruned_keys.size <= 3, includespk=[k1,k2] - JSON: adds
preset_name,preset_mode,preset_pruned_keys_count,preset_locked_domains_count, and optionallypreset_pruned_keys
FAQ
- How does namespacing interact with
enabled: false? Namespacing is ignored when disabled; tokens are used as declared. - Can I customize
locked_domains? Yes:SearchEngine.config.presets.locked_domains = %i[filter_by sort_by include_fields exclude_fields](Array/Set/single value accepted; normalized to Symbols). - What happens if a preset is missing on the server? Typesense returns an error at request time; handle it like any search error. The client surfaces it via
SearchEngine::Errors. - How do presets interact with field selection? In
:lock, selection keys (include_fields,exclude_fields) belong tolocked_domainsby default and are dropped from the chain; the preset governs them. - How does multi‑search resolve conflicting modes across entries? Modes are per entry. Each relation’s
preset_modeis applied independently when compilingsearches[].