Skip to content

Vector search

Vaultbase ships a built-in vector field type and cosine-similarity search on the standard records list endpoint. Single-binary, no extension to install, no external service to call — embeddings live next to the rest of your collection.

{ "name": "embedding", "type": "vector",
"options": { "dimensions": 1536 } }
  • dimensions — the vector length (1-4096). Every value stored on this field must be a number[] of exactly this length; non-numeric or wrong-length arrays fail validation with 422.
  • Stored as JSON-encoded number[] in the underlying SQLite column — no schema change beyond that.
GET /api/v1/docs?nearVector=[0.12,-0.05,...]&nearVectorField=embedding&nearLimit=10
ParamNotes
nearVectorJSON-encoded number[]. Length must match the field’s dimensions.
nearVectorFieldName of the vector field on this collection.
nearLimitTop-K results to return (max 1000, default 10).
nearMinScoreOptional minimum cosine similarity in [-1, 1]; matches below this are dropped.

The candidate set is filtered by filter + the collection’s list_rule first, so you can never rank rows the caller can’t otherwise see. After that, the candidates are ranked in-process by cosine similarity and returned sorted descending by _score.

{
"data": [
{ "id": "d1", "title": "...", "embedding": [...], "_score": 0.94 },
{ "id": "d2", "title": "...", "embedding": [...], "_score": 0.81 }
],
"page": 1, "perPage": 10, "totalItems": 10, "totalPages": 1
}

The v1 implementation is pure JS: candidates are pulled from SQLite under the existing access scope (capped at 10 000 rows per query), then ranked in process. This is fast enough for collections up to a few tens of thousands of vectors on a single host — the cosine loop is tight (O(n × dimensions) multiplications) and SQLite happily streams the candidate set.

For larger collections, the planned v2 path is to load the sqlite-vec extension via Database.loadExtension() and push the ANN search down into SQL. The query shape (?nearVector=…&nearVectorField=…&nearLimit=…) is stable across both implementations.

Vaultbase doesn’t bundle an embedding model — bring your own provider. Two common patterns:

Use helpers.http to call your provider on beforeCreate / beforeUpdate:

// beforeCreate on `docs`
if (typeof ctx.record.body === "string") {
const r = await ctx.helpers.http.postJson<{ data: [{ embedding: number[] }] }>(
"https://api.openai.com/v1/embeddings",
{ input: ctx.record.body, model: "text-embedding-3-small" },
{ Authorization: `Bearer ${ctx.helpers.os.env("OPENAI_API_KEY")}` },
);
ctx.record.embedding = r.data[0].embedding;
}

Compute the embedding on the client and PATCH it in. Useful when the embedding source is a raw upload and you’d rather not stream it through the server.

v1 (pure JS)v2 (sqlite-vec, planned)
Index buildNoneOn first write
Search costO(n × d) per queryO(log n + k × d)
MemoryBounded by candidate window (≤10K)Bounded by ANN index
Sweet spot< 50K vectorsMillions
SetupZeroExtension build per platform

If your collection grows past the v1 ceiling, the workaround is to shard across multiple collections (e.g. docs_2026q1, docs_2026q2) and union results client-side until v2 lands.