Search — Filters & mode
Vector search answers "closest to my query." Two knobs widen its reach: the
mode parameter (semantic vs keyword vs hybrid) and — for predicate-based
narrowing — the query() primitive that pairs naturally with search.
The three modes
| Mode | When to use | Behavior |
|---|---|---|
semantic | Natural-language queries, discovery, chat grounding | Pure vector similarity |
keyword | Exact tokens matter: SKUs, slugs, brand names | Full-text scoring only |
hybrid | Mixed intent: "nike running shoes", "react hooks tutorial" | Blends both scores |
Mode is a per-call choice — pass mode: 'keyword' | 'hybrid' on the
request, or reach for the sugar methods beam.<lens>.searchKeyword(...)
and beam.<lens>.searchHybrid(...). Default is semantic.
Narrowing with a predicate pre-query
Need to restrict candidates to "footwear that's in stock" before ranking?
Run a query() on the same lens to get matching IDs, then filter search
results client-side.
A single-call where predicate inside search() is on the roadmap — for
now combine query() + search(), or narrow via a joined lens using
include: { relationName: { where: ... } }.
Filtering by recency — changedSince / changedBefore
A built-in time-window filter lives on search(), similar(), and every
named feed. Pass an ISO-8601 timestamp and only rows whose
changeTrackingColumn value (default updated_at) falls in the window
are considered.
Both fields are independently optional. Omit them and nothing about your call changes — same SQL, same plan, same latency as before this feature existed. Use one or both as needed.
Where it works
| Surface | Supports filter | Notes |
|---|---|---|
beam.<lens>.search() / HTTP /v1/search/:lens | ✓ | All three modes: semantic, keyword, hybrid |
beam.<lens>.similar() / HTTP /v1/similar/:lens | ✓ | |
beam.<lens>.feed.<name>() / HTTP /v1/feed/:lens/:name | ✓ | Applies to the candidate pool; cursor/dedup unchanged |
WebSocket feed.next, stream.search | ✓ | Same field names; flows through params |
beam.<lens>.query() | — | Use a where: { updated_at: ... } predicate instead |
query() doesn't ship this filter because it's raw structured access
where you already have where to express anything you want. search /
similar / feed need it because they don't expose a where escape
hatch for semantic primitives.
CLI + Dev Console
Same filter, shorthand duration support (7d, 24h, 15m, 30s):
The Console dev drawer accepts the same --since / --before flags.
Behavior notes
- Tight windows still return a full
limit— the ranker keeps walking until it fills the page, rather than stopping early and returning fewer results than asked for. - Omitting both bounds is a true no-op path: same query plan, same latency as before the filter existed.