Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SINDI_V2

SINDI_V2 is VSAG’s term-first sparse-vector index. It uses the same windowed inverted-list scoring model and 1 - inner_product distance as SINDI, but persists postings by term so a search can read only the lists needed by the query.

  • Factory entry: sindi_v2
  • Source: src/algorithm/sindi_v2/
  • Required dtype: sparse
  • Required metric_type: ip

Storage layout

Each term payload stores only non-empty windows:

non_empty_window_count
ordered {window_id, posting_count}[]
local_doc_ids[]
alignment padding
encoded_values[]

At query time, SINDI_V2 expands the sparse metadata into window_count + 1 window offsets. A query loads postings only for its retained terms through reader_io, mmap_io, buffer_io, async_io, or an in-memory backend.

This differs from SINDI’s window-first serialization. The two factory entries therefore require different persisted formats and must not be used interchangeably when loading an index.

Quick start

#include <vsag/vsag.h>

std::string params = R"({
    "dtype": "sparse",
    "metric_type": "ip",
    "dim": 1024,
    "index_param": {
        "term_id_limit": 30000,
        "window_size": 50000,
        "doc_prune_ratio": 0.1,
        "use_quantization": true,
        "use_reorder": true,
        "remap_term_ids": true,
        "term_io": {
            "type": "buffer_io",
            "file_path": "/tmp/sindi_v2.terms"
        },
        "rerank_io": {
            "type": "block_memory_io"
        }
    }
})";
auto index = vsag::Factory::CreateIndex("sindi_v2", params).value();
index->Build(base);

auto result = index->KnnSearch(
    query, 10,
    R"({"sindi_v2": {
        "n_candidate": 100,
        "filter_callback_limit": 10000,
        "query_prune_ratio": 0.1,
        "term_prune_ratio": 0.2,
        "term_retain_threshold": 10000
    }})").value();

Build parameters

Build parameters live under index_param.

ParameterTypeDefaultDescription
dimintrequiredMaximum number of non-zero elements per sparse vector, not the vocabulary size.
term_id_limitint1000000Upper bound on term IDs; maximum 50 000 000.
window_sizeint50000Documents per window, from 10 000 to 60 000.
doc_prune_ratiofloat0.0Fraction of lowest-weight document terms removed during build ([0.0, 1.0)).
use_quantizationbool or stringfalsefalse stores FP32, true stores SQ8, and "fp16" stores FP16 values.
use_reorderboolfalseStore high-precision vectors and rerank coarse candidates.
rerank_typestring"fp32"Rerank storage type: fp32, fp16, or dmq8. FP16 stores document values in half precision and scores in FP32.
dmq_shared_codebook_thresholdint1024Low-frequency term threshold for the shared DMQ codebook.
remap_term_idsboolfalseCompact sparse or widely separated external term IDs.
avg_doc_term_lengthint100Memory-estimation hint only.
immutableboolfalseSelect the compact read-only in-memory term DataCell.
term_ioobject{"type": "reader_io"}Backend used for term-first postings after load.
rerank_ioobject{"type": "block_memory_io"}Backend used for rerank vectors.
rerank_layoutuint320Number of leading terms in the top-terms-signature layout; 0 disables it.

File-backed term_io supports mmap_io, buffer_io, and async_io, and requires file_path. If a file-backed rerank_io omits file_path, VSAG derives it as <term_io.file_path>.rerank.

rerank_type: "fp16" requires use_reorder: true and supports the same rerank I/O and layout options as fp32. rerank_layout > 0 requires use_reorder: true. rerank_type: "dmq8" requires rerank_layout: 0 and the default block_memory_io rerank backend.

Host filtering

SINDI_V2 supports the same string host build and KNN-query metadata contract as SINDI. Both mutable and immutable indexes are supported. It uses the shared host grouping and routing component while retaining its own term-first posting storage. Host membership is enforced during posting-window search, including multiple ranges created by mutable Add(). The empty-string missing-host value, persisted internal dictionary, mutable Add() metadata rules, streaming serialization, and the KNN-only scope are the same as SINDI.

Publish-time filtering

SINDI_V2 supports the same SINDI Unix-second input, UTC-day matching, and inclusive time-range contract. The two indexes share one metadata filtering component for time-only, host-only, and combined time-and-host KNN routing. SINDI_V2 retains its term-first posting layout. Publish-time filtering supports mutable and immutable indexes with either use_reorder setting and is preserved by standard and streaming serialization; range search remains unfiltered. A mutable time-aware SINDI_V2 is build-once and follows the same initial Build() or first-Add() and later-Add() rejection rules as SINDI. Base timestamp 0 uses the same missing-time semantics: unfiltered and host-only queries include those documents, while time queries exclude them. Exact day filtering disables term-level posting pruning in each selected window, so time queries may scan more postings than host-only queries. Host-only queries on time-enabled indexes share the boundary-window behavior documented for SINDI.

Search parameters

Search parameters live under {"sindi_v2": {...}}.

ParameterTypeDefaultDescription
n_candidateint0Coarse candidate count; 0 uses the index default derived from topk.
filter_callback_limituint640Maximum user Filter::CheckValid callback invocations for one filtered search. 0 disables the limit. Reaching a positive limit stops candidate and window scanning after processing the final callback result and returns the candidates accepted so far, so the result may be partial. The limit applies to the regular KNN and range-search APIs.
query_prune_ratiofloat0.0Fraction of the lowest-weight query terms skipped ([0.0, 1.0)).
term_prune_ratiofloat0.0Fraction of the lowest stored values skipped in each term list ([0.0, 1.0)).
term_retain_thresholduint640Maximum postings for one term across all windows. 0 disables the limit; positive values allow each non-empty window posting list to scan at most max(1, floor(threshold / window_count)) postings.

After combining the ratio and threshold limits, SINDI V2 scans at least one posting from every non-empty term list.

SINDI V2 chooses the heap-insertion strategy automatically from the build-time doc_prune_ratio and search-time query_prune_ratio. With the current 0.1 threshold, it uses distance-array insertion when both ratios are <= 0.1; if either ratio is greater than 0.1, it uses term-list heap insertion.

Choosing between SINDI and SINDI_V2

Use SINDI when the full sparse index remains in memory and window-first serialization is appropriate. Use SINDI_V2 when persisted postings should be organized by term or when searches should fetch only the query’s term lists from external storage.