Qirava DMS docs
Vector: approximate nearest neighbor (LSH k-NN)
Vector similarity search uses NEAREST <field> TO [<vector>] LIMIT k. The engine ranks rows by cosine similarity to the query vector and returns the k closest. Vectors ingest as plain numeric list literals ([1.0, 0.0]), so there is no separate vector literal syntax. On small tables (≤ 256 rows) the engine scans exactly. On larger tables with a Vector index it uses random-hyperplane LSH (locality-sensitive hashing) with multi-probe to gather candidate rows, then ranks those candidates by EXACT cosine — so within the candidate set the order is exact and only recall is approximate. This page covers the syntax, the LSH ANN path, the small-table brute-force fallback, and hybrid (filtered) vector search.
NEAREST syntax#
NEAREST <field> TO [<numbers>] LIMIT k ranks rows by cosine similarity of <field> to the query vector and returns the top k. The query vector is a numeric list literal; the value of k comes from LIMIT. The stored field may be a native Vector or a List of numbers — both are coerced to f32. Rows whose field is not a numeric vector are skipped.
SELECT name FROM items NEAREST v TO [1.0, 0.0] LIMIT 2Small-table brute force (exact)#
Below 256 rows (the VECTOR_ANN_MIN threshold) — or when there is no vector index on the field — the engine scans every row and computes exact cosine, returning the true nearest. ANN is not worth it at small scale and exactness is cheap.
Request
-- items: a v[1,0]; b v[0,1]; c v[0.9,0.1]
SELECT name FROM items NEAREST v TO [1.0, 0.0] LIMIT 2Response
// nearest to [1,0]: "a" then "c" (then "b"); LIMIT 2 returns a and c, b excluded
{ "error": null, "data": [ { "name": "a" }, { "name": "c" } ], "root": { "count": 2 } }LSH approximate nearest neighbor#
On a large table with a Vector index, each row's vector is hashed into a bucket by random-hyperplane LSH: 16 hyperplanes split the space, and a vector's 16-bit hash is the sign pattern of its dot products with the planes. Cosine-similar vectors collide into the same or a nearby bucket. The planes are seeded deterministically from the index name, so rebuilding the index reproduces the same buckets.
At query time the engine hashes the query vector, gathers its bucket, then multi-probes nearby buckets at Hamming distance 1 and 2 (flipping 1 or 2 hash bits) until it has gathered enough candidates (target = max(k×16, 64)). If too few candidates surface (fewer than k), it falls back to the exact scan. The gathered candidates are then ranked by EXACT cosine and the top k returned.
query vec ──hash(16 planes)──▶ bucket qh
gather(qh)
radius 1: gather(qh ^ 1bit) ... ┐ until candidates >= k*16 (min 64)
radius 2: gather(qh ^ 2bits) ... ┘
if candidates < k: fall back to exact scan
else: rank candidates by exact cosine, take kRequest
CREATE TABLE items SCHEMALESS
CREATE INDEX v_ann ON items (v) VECTOR
-- insert 400 varied positive vectors, plus one exact match:
INSERT INTO items { name: "target", v: [1.0, 0.0, 0.0, 0.0] }
SELECT name FROM items NEAREST v TO [1.0, 0.0, 0.0, 0.0] LIMIT 1Response
{ "error": null, "data": [ { "name": "target" } ], "root": { "count": 1 } }Hybrid (filtered) vector search#
Combine NEAREST with WHERE for RAG-style retrieval restricted to a metadata subset. The filter is applied to the vector candidates BEFORE cosine ranking, so a globally-closest row that fails the filter is correctly excluded.
Request
-- docs: a cat x v[1,0]; b cat y v[0.99,0.01] (closest overall); c cat x v[0.8,0.2]
SELECT name FROM docs NEAREST v TO [1.0, 0.0] WHERE cat = "x" LIMIT 2Response
// b (cat y) is closest overall but filtered out; result is the cat=x rows a and c
{ "error": null, "data": [ { "name": "a" }, { "name": "c" } ], "root": { "count": 2 } }