VectorStore
Store dense, sparse, and hybrid vectors for retrieval and semantic lookup.
VectorStore is Crux's similarity-search interface.
Use it when a feature needs dense, sparse, or hybrid vector search. Keep JSON records in DataStore, then store searchable vectors in VectorStore using the same keys.
import { inMemoryDataStore, inMemoryVectorStore, storage } from '@crux/core/storage'
const data = inMemoryDataStore()
const vectors = inMemoryVectorStore()
const appStorage = storage({ data, vectors })Interface
import type { VectorStore } from '@crux/core/storage'
const vectors: VectorStore = {
async upsert(records) {
// records: { key, dense?, sparse?, metadata? }[]
},
async delete(keys) {
// delete vector records by key
},
async search(query) {
// query: { dense?, sparse?, fusion?, limit?, threshold?, filter? }
return []
},
capabilities() {
return { dense: true, sparse: true, hybrid: true }
},
}search() returns keys and scores. Crux hydrates full records through DataStore when it needs document content or metadata.
Dense, Sparse, Hybrid
Dense search uses one dense vector:
await vectors.search({
dense: [0.12, 0.4, 0.9],
limit: 10,
})Sparse search uses token indices and weights:
await vectors.search({
sparse: {
indices: [12, 98, 322],
values: [0.8, 0.3, 0.6],
},
limit: 10,
})Hybrid search sends both:
await vectors.search({
dense: [0.12, 0.4, 0.9],
sparse: {
indices: [12, 98, 322],
values: [0.8, 0.3, 0.6],
},
fusion: 'dbsf',
limit: 10,
})Unsupported modes should throw clearly. Never silently degrade hybrid to dense-only or sparse-only.
With Retriever
Most users do not call VectorStore directly. They pass data and vectors to retriever():
const docs = retriever({
id: 'docs',
namespace: 'product-docs',
data,
vectors,
dense,
sparse,
search: { mode: 'hybrid', fusion: 'dbsf', limit: 8 },
})The retriever handles embedding the query, searching vectors, hydrating records from data, and returning hits.
Bundled Vector Stores
Use inMemoryVectorStore() for tests and examples:
import { inMemoryVectorStore } from '@crux/core/storage'
const vectors = inMemoryVectorStore()Use Upstash Vector for production dense, sparse, or hybrid retrieval:
import { upstashVectorStore } from '@crux/upstash'
import { Index } from '@upstash/vector'
const vectors = upstashVectorStore({
index: new Index({
url: process.env.UPSTASH_VECTOR_REST_URL!,
token: process.env.UPSTASH_VECTOR_REST_TOKEN!,
}),
namespace: 'product-docs',
})See Retrieval for the user-facing retrieval APIs, Upstash storage for the bundled hybrid-capable adapter, and the Postgres storage cookbook for a dense pgvector example.
Custom VectorStore
Implement VectorStore when your vector database can upsert records by key and search them by dense, sparse, or hybrid query.
import type { VectorStore } from '@crux/core/storage'
export function myVectorStore(): VectorStore {
return {
async upsert(records) {
await vectorDb.upsert(records.map((record) => ({
id: record.key,
vector: record.dense,
sparseVector: record.sparse,
metadata: record.metadata,
})))
},
async delete(keys) {
await vectorDb.delete(keys)
},
async search(query) {
const results = await vectorDb.query({
vector: query.dense,
sparseVector: query.sparse,
topK: query.limit ?? 10,
fusion: query.fusion,
})
return results.map((result) => ({
key: result.id,
score: result.score,
metadata: result.metadata,
}))
},
}
}If your vector database also stores full JSON documents, you can expose that separately as a DataStore. The public Crux contract stays clear: data hydrates records; vectors find similar records.