Visor
Visor is ZVN's own search engine, and the fourth engine Headband runs beside Meilisearch, Typesense and Elasticsearch. One Visor index answers in two ranking modes, picked per request: instant for typo-tolerant, as-you-type search, and retrieval (BM25F) for longer queries. The same REST API, keys and dashboard work on it as on the other engines.
Headband runs Visor for you on dedicated engines. There is no bring-your-own Visor.
Choose Visor
Pick Visor wherever you pick an engine:
- In the dashboard: New index, Create engine on the Engines page, or the default engine of a new project.
- In the API:
"engine": "visor"onPOST /v1/indexes,POST /v1/instancesandPOST /v1/projects. - In the CLI, as below.
headband instances create visor --lane serverless
headband indexes create products --engine visor
headband projects create shop --engine visorAn index created with no instanceId goes on your default Visor engine. Headband creates it on first use, serverless at 512 MB, as it does for Meilisearch.
Where Visor Runs
Visor is offered in a region where a dedicated engine host runs it. The dashboard lists it there, and only there. In a region where no host runs it, creating a Visor engine or a Visor index answers 503 engine_not_available with the message Visor is not available in ewr1 yet. (naming the region). Nothing is created.
| Visor | |
|---|---|
| Lanes | Serverless (parks when idle, wakes on the next request) and always on |
| Sizes (memory) | 512 MB to 32 GB |
| Serverless idle timeout | 300 seconds by default, 60 seconds to 24 hours |
| Bring your own engine | No. engineConfig with visor answers 400 invalid_engine_config |
| Managed Sync | Not yet. Sync writes to Meilisearch, Typesense and Elasticsearch engines |
Search
POST /v1/search takes mode on a Visor index. Leave it out for instant. Other engines ignore it.
{
"index": "products",
"q": "trail running shoes waterproof",
"mode": "retrieval",
"filter": "price < 200",
"facets": ["brand"],
"attributesToHighlight": ["*"],
"limit": 20
}Filters (Meilisearch syntax, as a string or an array), sort, facets, highlighting (named fields or *), cropping, attributesToRetrieve and showRankingScore work as on Meilisearch. A field must be filterable or sortable in the settings before a filter or sort can use it. Retrieval mode ranks by relevance only: a sort with it answers 400 invalid_search_sort.
Visor crops to 20 words, matches as matchingStrategy "last" does, and returns no match positions. Those values are accepted. Any other cropLength, a matchingStrategy of "all" or "frequency", and showMatchesPosition: true answer 400 feature_not_supported, so a parameter never looks applied when it is not.
Visor has no vector search yet. hybrid or vector on /v1/search answers 400 feature_not_supported, and /v1/hybrid-search runs keyword-only and says so with "fallback": true.
Documents and Tasks
Writes are synchronous. A write is searchable when it returns, so its task has taskUid 0 and status succeeded, and there is nothing to poll.
A batch is one atomic write: a bad document fails the whole request. A bulk import (POST /v1/bulk) is split into batches that fit the engine's request size, and a batch the engine still refuses is halved and sent again. A single POST /v1/documents is never split, so one that is too large answers 413 payload_too_large.
Listing documents by offset on GET /v1/documents pages within the first 1000 documents. A walk by cursor reads every document, in primary key order, and the dashboard's Documents tab walks that way. Search reaches every document either way.
A search counts its hits within the first 1000 too. Past them, an empty query with no filter answers the index's document count in estimatedTotalHits, and any other search answers 1000 with "totalHitsExact": false: at least that many hits match.
Visor makes an index's primary key filterable and sortable as soon as the key is known (when the index is created with it, or on the first write when Visor infers it), and a settings update keeps the key in both lists. An index from before that may lack them: a walk past 1000 documents then answers 400 feature_not_supported naming the settings to add, and adding them rebuilds the index once.
Settings
These settings apply to a live Visor index: searchableAttributes, filterableAttributes, sortableAttributes, displayedAttributes, rankingRules (the built-in rules), typoTolerance, synonyms and stopWords. Sending null resets one to Visor's default.
Visor refuses distinctAttribute, faceting, pagination, separatorTokens, nonSeparatorTokens, dictionary, proximityPrecision, searchCutoffMs and embedders with 400 feature_not_supported, so a setting never looks applied when it is not.
Errors
| Status | Code | When |
|---|---|---|
| 404 | index_not_found | The index does not exist. |
| 400 | invalid_search_filter | A malformed filter, or a filter on a field that is not filterable. |
| 400 | feature_not_supported | A setting or search parameter Visor does not have, such as vector search. |
| 413 | payload_too_large | One write request is larger than the engine accepts. |
| 503 | storage_unavailable, search_queue_full | Busy or briefly unavailable. Retryable: the answer has Retry-After. |
| 503 | engine_not_available | No dedicated engine host in the region runs Visor yet. |
See also