API reference · More
VectorIndex
Exact nearest-neighbour search over embeddings, on every core, with filters on any other column.
NodeBrowserEdge
A VectorIndex stores embeddings as float32 columns of a table, v0 to v{dim-1}, next to any other columns you give it. A search computes the score as an ordinary fused expression with the query as constants, 24 dimensions per pass, then picks the best with top(k). It is exact, not approximate: there is no index to build or tune, and the results are those of brute force. Because it is a table, filters work, and on a SplitEngine every worker searches its own rows in one round trip. Vector search is experimental: the API may change. See Vector search.
import { Engine, VectorIndex } from 'vectrojs'; // also 'vectrojs/browser', 'vectrojs/portable'
VectorIndex.create(engine, options)
Builds the index and returns a promise of it. engine is an Engine (Node, browser or portable) or a SplitEngine.
| Name | Type | Default | Meaning |
|---|---|---|---|
vectors |
number[][] or a typed array |
required | The embeddings: one array per row, or one flat array of n * dim values. |
dim |
number | required | The length of each embedding. |
metric |
'cosine', 'dot' or 'l2' |
'cosine' |
How closeness is scored (below). |
columns |
object | {} |
Other columns, one value per vector, returned with each hit and usable in filters. Names v0, v1… and names starting with __np_ are reserved. |
| Metric | Score | Best |
|---|---|---|
'cosine' |
The dot product of the normalized vectors (vectors are normalized when stored, the query when searched). | Highest |
'dot' |
The dot product. | Highest |
'l2' |
The squared Euclidean distance. | Lowest |
index.searchAsync(query, options?)
The k rows closest to query (an array of dim numbers), among the rows matching filter, best first. Returns a promise of [{ ...columns, score }].
| Name | Type | Default | Meaning |
|---|---|---|---|
k |
number | 10 | How many hits. |
filter |
string | none | An expression over the other columns, as in table.filter. |
params |
object | {} |
Values for names in filter that aren’t columns. |
There is no sync form.
index.length
A getter: the number of vectors.
index.dispose()
Frees the index’s table. Returns a promise.
The index also has the properties table (the underlying table or split table), dim, metric and names (the names of the other columns).
import { Engine, VectorIndex } from 'vectrojs';
const engine = new Engine();
const docs = [
{ id: 1, year: 2019, v: [0.9, 0.1, 0.0] },
{ id: 2, year: 2021, v: [0.1, 0.9, 0.1] },
{ id: 3, year: 2022, v: [0.7, 0.6, 0.1] },
{ id: 4, year: 2023, v: [0.0, 0.2, 0.9] },
];
const index = await VectorIndex.create(engine, {
vectors: docs.map((d) => d.v),
dim: 3,
columns: { id: docs.map((d) => d.id), year: docs.map((d) => d.year) },
});
console.log(index.length, index.metric);
console.log(await index.searchAsync([1, 0.2, 0], { k: 2 }));
console.log(await index.searchAsync([1, 0.2, 0], { k: 2, filter: 'year >= since', params: { since: 2021 } }));
await index.dispose();
await engine.close();
import { Engine, SplitEngine, VectorIndex } from 'vectrojs';
const dim = 16, n = 20_000;
const flat = new Float32Array(n * dim);
let seed = 1;
for (let i = 0; i < flat.length; i++) flat[i] = (seed = (seed * 16807) % 2147483647) / 2147483647;
const query = Array.from(flat.subarray(123 * dim, 124 * dim));
const columns = { row: Float64Array.from({ length: n }, (_, i) => i) };
const engine = new Engine();
const near = await VectorIndex.create(engine, { vectors: flat, dim, metric: 'l2', columns });
console.log(await near.searchAsync(query, { k: 3 })); // row 123 first, score 0
const split = new SplitEngine({ workers: 2 });
const spread = await VectorIndex.create(split, { vectors: flat, dim, metric: 'l2', columns });
console.log(await spread.searchAsync(query, { k: 3 })); // the same hits
await near.dispose();
await spread.dispose();
await split.close();
await engine.close();