You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Feature Request: Sparse Vector Index (LSM_SPARSE_VECTOR)
Originated from discussion #4044 (Qdrant to ArcadeDB migration).
Overview
Add a native sparse vector index type, LSM_SPARSE_VECTOR, that persists sparse embedding vectors and serves top-K dot-product retrieval. The index reuses the LSM-Tree storage backbone already used by FULL_TEXT and LSM_VECTOR, so it inherits ACID, WAL, HA, and compaction without introducing a separate storage engine.
ArcadeDB already ships the in-memory primitives (SparseVector class and the vector.sparseCreate, vector.sparseDot, vector.denseToSparse, vector.sparseToDense SQL functions) but has no persistent index backing them. This issue adds the missing piece.
Use cases (intentionally generic, not tied to one model):
Posting-list inverted index, keyed by sparse dimension id. The composite key {int dim_id, RID rid, float weight} sorts postings within a dim by RID ascending, which is the order required by WAND-style document-at-a-time scoring.
Per-dim max_weight upper bound is maintained as a sidecar map (lazily populated on first WAND query, then kept monotone via put; deletes leave it conservatively high, still a valid upper bound).
Mutable LSM segments accept inserts; compaction produces immutable RID-sorted segments naturally because the LSM-Tree backbone already does this.
Optional IDF statistics in the index metadata enable the IDF modifier.
The tighter per-entry max_next_weight (BlockMax-WAND) is intentionally deferred to #4068 along with weight quantization and parallel per-segment scoring; those are the steps that scale the index from the 2-3K-vector use case in #4044 to the 100M+ regime.
Property persistence
Two parallel properties on the indexed type, using the existing Type.ARRAY_OF_INTEGERS and Type.ARRAY_OF_FLOATS:
<name>_indices of type ARRAY_OF_INTEGERS
<name>_values of type ARRAY_OF_FLOATS
This avoids introducing a new Type.SPARSE_VECTOR and matches how LSM_VECTOR already operates on ARRAY_OF_FLOATS.
Schema API
SQL (keyword is LSM_SPARSE_VECTOR, matching the LSM_VECTOR precedent and the enum name):
Document-at-a-time min-heap with WAND pivot-based skipping. Per-dim cursor sorted by current RID; pivot is the smallest cursor index where the prefix sum of upper bounds exceeds the K-th best score so far. Cursors below the pivot seek forward to the pivot's RID; aligned cursors at the pivot score the doc and advance.
Only dot-product similarity is supported (Qdrant carries the same restriction; cosine on sparse is handled by L2-normalizing both query and stored vectors at insert time).
Acceptance criteria
New Schema.INDEX_TYPE.LSM_SPARSE_VECTOR enum entry.
Top-K dot-product retrieval with WAND-style pruning.
SQL function vector.sparseNeighbors(indexSpec, indices, values, K, options).
Optional IDF modifier.
Persistence test (persistAcrossReopenAndQueryStillWorks).
Concurrency test (LSMSparseVectorIndexConcurrencyTest, @Tag("slow")).
Correctness test: results match brute-force vector.sparseDot baseline (createIndexViaJavaApiAndQueryTopK + wandResultsMatchBruteForceOnLargerCorpus).
Benchmark vs. brute-force on 100k+ vectors (LSMSparseVectorIndexBenchmark, @Tag("benchmark")). The MVP WAND with only dim-level upper bounds does not yet beat plain scan at 100k with skewed query distributions; the BlockMax-WAND step in feat: WAND/BlockMax-WAND dynamic pruning for LSM_SPARSE_VECTOR (scale to 100M+) #4068 is what closes the gap at this scale.
Out of scope (future work)
L2 / cosine for sparse (use pre-normalized vectors instead).
Feature Request: Sparse Vector Index (LSM_SPARSE_VECTOR)
Originated from discussion #4044 (Qdrant to ArcadeDB migration).
Overview
Add a native sparse vector index type,
LSM_SPARSE_VECTOR, that persists sparse embedding vectors and serves top-K dot-product retrieval. The index reuses the LSM-Tree storage backbone already used byFULL_TEXTandLSM_VECTOR, so it inherits ACID, WAL, HA, and compaction without introducing a separate storage engine.ArcadeDB already ships the in-memory primitives (
SparseVectorclass and thevector.sparseCreate,vector.sparseDot,vector.denseToSparse,vector.sparseToDenseSQL functions) but has no persistent index backing them. This issue adds the missing piece.Use cases (intentionally generic, not tied to one model):
opensearch-neural-sparse-encoding-multilingual-v1).Design
Storage layout
Posting-list inverted index, keyed by sparse dimension id. The composite key {
int dim_id,RID rid,float weight} sorts postings within a dim by RID ascending, which is the order required by WAND-style document-at-a-time scoring.max_weightupper bound is maintained as a sidecar map (lazily populated on first WAND query, then kept monotone viaput; deletes leave it conservatively high, still a valid upper bound).The tighter per-entry
max_next_weight(BlockMax-WAND) is intentionally deferred to #4068 along with weight quantization and parallel per-segment scoring; those are the steps that scale the index from the 2-3K-vector use case in #4044 to the 100M+ regime.Property persistence
Two parallel properties on the indexed type, using the existing
Type.ARRAY_OF_INTEGERSandType.ARRAY_OF_FLOATS:<name>_indicesof typeARRAY_OF_INTEGERS<name>_valuesof typeARRAY_OF_FLOATSThis avoids introducing a new
Type.SPARSE_VECTORand matches howLSM_VECTORalready operates onARRAY_OF_FLOATS.Schema API
SQL (keyword is
LSM_SPARSE_VECTOR, matching theLSM_VECTORprecedent and the enum name):Java:
Query API
New SQL function
vector.sparseNeighbors, mirroringvector.neighbors:Options map (forward-compatible with the groupBy and fusion issues below):
filter: list of allowed RIDs (post-filter).groupBy,groupSize: see feat: groupBy/groupSize options on vector.neighbors for diversified retrieval #4067.Top-K algorithm
Document-at-a-time min-heap with WAND pivot-based skipping. Per-dim cursor sorted by current RID; pivot is the smallest cursor index where the prefix sum of upper bounds exceeds the K-th best score so far. Cursors below the pivot seek forward to the pivot's RID; aligned cursors at the pivot score the doc and advance.
Only dot-product similarity is supported (Qdrant carries the same restriction; cosine on sparse is handled by L2-normalizing both query and stored vectors at insert time).
Acceptance criteria
Schema.INDEX_TYPE.LSM_SPARSE_VECTORenum entry.CREATE INDEX ... LSM_SPARSE_VECTORsyntax (withMETADATA { dimensions, modifier }).withSparseVectorType()+.withDimensions()+.withModifier().max_weightupper bound. (Tighter per-entrymax_next_weightdeferred to feat: WAND/BlockMax-WAND dynamic pruning for LSM_SPARSE_VECTOR (scale to 100M+) #4068 Step 3.)vector.sparseNeighbors(indexSpec, indices, values, K, options).persistAcrossReopenAndQueryStillWorks).LSMSparseVectorIndexConcurrencyTest,@Tag("slow")).vector.sparseDotbaseline (createIndexViaJavaApiAndQueryTopK+wandResultsMatchBruteForceOnLargerCorpus).LSMSparseVectorIndexBenchmark,@Tag("benchmark")). The MVP WAND with only dim-level upper bounds does not yet beat plain scan at 100k with skewed query distributions; the BlockMax-WAND step in feat: WAND/BlockMax-WAND dynamic pruning for LSM_SPARSE_VECTOR (scale to 100M+) #4068 is what closes the gap at this scale.Out of scope (future work)
dfper query; feat: WAND/BlockMax-WAND dynamic pruning for LSM_SPARSE_VECTOR (scale to 100M+) #4068 will maintain it incrementally).Related
max_next_weight, quantization, parallel segments)vector.fuse), feat: groupBy/groupSize options on vector.neighbors for diversified retrieval #4067 (groupBy onvector.neighbors).cc @astarso