Skip to content

Vector search

Vector search adds semantic retrieval to a collection: configure which field holds the embedding and where its text comes from, and Groveback embeds documents on write and ranks them on query.

Terminal window
curl -X PUT "$URL/api/v1/admin/collections/articles/vector-config" \
-H "authorization: Bearer $ADMIN_KEY" -H 'content-type: application/json' \
-d '{
"fields": [{
"field": "embedding",
"dimensions": 384,
"similarity": "cosine",
"source": "body",
"mode": "byo",
"provider": "local",
"filterFields": ["status", "tenantId"]
}]
}'
Key Meaning
field The logical name of the vector field.
dimensions 1–4096. Must match the provider’s model.
similarity cosine, euclidean or dotProduct.
source The document field whose text gets embedded.
mode byo or auto-embed — see below.
provider / model Which embedder to use.
filterFields Fields indexed for exact pre-filtering during search.

In the bundle this lives at groveback/vectors/<collection>.json, keyed by collection.

mode: "byo" — Groveback embeds your source field through a provider and stores the vector itself. Works with any backend.

mode: "auto-embed" — MongoDB does the embedding natively (the provider is a native model id) and the search reads the source field directly. Requires a MongoDB deployment that supports it.

Provider Notes
mock Deterministic and offline — hash-based, not semantic. Only identical-text ranking is meaningful. For tests.
local An OpenAI-compatible endpoint or HuggingFace TEI, via EMBEDDINGS_URL.
openai Needs OPENAI_API_KEY.
voyage Needs VOYAGE_API_KEY.

make dev-vector brings up the full local stack including a TEI embeddings service.

On a write, a subscriber embeds the source field and stores the vector at __vec_<field> plus a hash of the source at __vec_<field>_hash. The hash is the loop guard — an unchanged source is not re-embedded.

Skipped silently when there is no provider, the source is empty, or the source is not a string. At most four embeds are in flight at once; excess events are dropped.

Terminal window
curl -X POST "$URL/api/v1/articles/search" \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"text":"how do policies work","limit":5,"filter":{"status":"published"},
"fields":["title","url"]}'
const hits = await client.collection('articles').search({
text: 'how do policies work',
limit: 5,
});
// each hit carries _score
Field Meaning
text Embedded server-side. Auto-embed backends require this and reject a precomputed vector.
vector A precomputed embedding.
limit Result count.
filter A Mongo-style pre-filter, on top of the policy filter.
fields Inclusion projection; id and _score are always returned.
field Which vector field to search when several are configured. Defaults to the first.

Naming an unknown vector field is an explicit validation error, not a silent fallback to the default.

Search is authorized by the list rule, and the policy filter post-filters the candidates. It is scoped as a read operation, so a read-scoped API key can run it.

Because projection is applied last, a fields list can only ever narrow what you are already allowed to see.

Two entry points: the generic vectorSearch field, and a typed <collection>_search field on every collection with a vector config.

query {
articles_search(text: "policies", limit: 5) {
id
title
}
}

On hosted deployments a vectors gauge counts stored BYO embeddings. At quota, new embeddings are skipped while the document write itself proceeds untouched; re-embeds always go through, so an edit never breaks. auto-embed fields are not metered.

Under grove dev without --mongo, vector search works only with a supplied query vector — embedding text at query time needs MongoDB. Point at a real Mongo for full behavior:

Terminal window
grove dev --mongo mongodb://localhost:27017/dev