FeOx Vector
Embedded vector store for Rust, built on FeOxDB. Exact and approximate cosine search with metadata filtering, in your process, with no server to run.
Documentation | FeOxDB | feox-ann | Issues
Features
- Exact and ANN Queries: Brute-force scans for small or heavily filtered collections, HNSW (via feox-ann) for large ones. Selectable per query.
- SIMD Scoring: Cosine similarity runs on feox-ann's NEON (aarch64) and AVX2+FMA (x86_64, runtime-detected) dot-product kernels, on both the exact and ANN paths.
- Metadata Filtering:
$eq,$in,$ninoperators compiled against an inverted facet index. Small filtered candidate sets are re-ranked exactly instead of traversing the ANN graph, so selective filters return exact results. - Lock-Free Index Refresh: ANN snapshots publish through
arc-swap. Writes mark a scope dirty and a background thread rebuilds while queries keep flowing. Until a fresh snapshot is ready, ANN queries fall back to exact scans so completed writes and deletes are immediately visible. - Deterministic Output: feox-ann builds reproducible graphs, and results rank by score, then recency, then id. Identical data returns identical output.
- Namespaced Collections: Records scope by
namespace / index / partition. One store serves many collections. - Memory or Disk: Run fully in memory, or point FeOxDB at a file for persistence with write-behind buffering.
- Compact Records: Vectors are stored in a binary format (magic
FXV1) with little-endian f32 values and JSON metadata. Vector data is decoded without JSON parsing.
Quick Start
[]
= "0.1"
use ;
Persistent mode:
let store = open?;
let vectors = new;
// Flush at application commit points that must survive abrupt process exit.
vectors.flush?;
Metadata Filters
Filters are a map from metadata key to either a literal (equality) or an operator object:
use json;
let mut filter = new;
filter.insert; // equality
filter.insert; // any of
filter.insert; // none of
In ANN mode, filters compile against an inverted facet index built alongside the graph. When the filtered candidate set is small (at most 4,096 ids), the query skips the graph entirely and re-ranks those candidates exactly. Selective filters therefore return exact results.
ANN Mode
use ;
// Explicit build (blocking), with default or custom HNSW parameters:
vectors.rebuild_ann?;
vectors.rebuild_ann_with_config?;
// Query:
let result = vectors.query?;
The first ANN query on a scope schedules a background build and answers exactly in the meantime. Writes mark the scope dirty and trigger a fresh rebuild on the next ANN query. Dirty and rebuilding scopes answer exactly until the fresh snapshot publishes, preserving read-after-write semantics without locking query execution.
Note: ANN snapshots are in-memory and rebuilt from stored records on process start (first ANN query per scope). Records themselves persist via FeOxDB.
Benchmarks
Criterion benches populate 10,000 x 128d records from a seeded generator and measure batched upsert throughput plus top-10 query latency for exact, ANN, and filtered-ANN modes. Deterministic inputs make numbers comparable across machines and commits.
Reference numbers on Apple Silicon, single-threaded, 10k x 128d with metadata:
| Operation | Result |
|---|---|
| Batched upsert | ~13.5 ms for 10k records (~740k records/s) |
| ANN query top-10 | ~102 us |
| Exact scan top-10 (full 10k) | ~3.8 ms |
| Filtered ANN (filter matches 2,500 ids, exact re-rank path) | ~1.6 ms, exact results |
Limits
Inputs are validated with practical ceilings: at most 128 records per upsert, 4,096 dimensions, 16 KB metadata per record, and 100 results per query. Segment names (namespace, index, partition, ids) must be non-empty, /-free, and at most 512 bytes.
License
Licensed under the Apache License, Version 2.0. See LICENSE.
Contributing
Contributions are welcome! See CONTRIBUTING.md.