Skip to content

Embedding Storage Backends

omop-emb selects the storage backend at runtime via the OMOP_EMB_BACKEND environment variable (default: sqlitevec).

Supported backends

Backend Value Requires Notes
sqlite-vec sqlitevec nothing extra Default. File or in-memory.
pgvector pgvector omop-emb[pgvector] + PostgreSQL Scales to large corpora. HNSW indexing and halfvec storage.

FAISS is a sidecar, not a backend

FAISS (omop-emb[faiss-cpu]) is a read-acceleration layer that sits on top of sqlite-vec or pgvector. It is not a primary backend and cannot be selected via OMOP_EMB_BACKEND. See the CLI reference for how to export and use FAISS indices.

Runtime selection

export OMOP_EMB_BACKEND=sqlitevec   # default — set OMOP_EMB_SQLITE_PATH
export OMOP_EMB_BACKEND=pgvector    # set OMOP_EMB_DB_* vars or OMOP_EMB_DB_URL

See Installation for the full list of connection variables for each backend.

Index types

Each primary backend supports index types controlled by an IndexConfig object.

Registration always uses FLAT

Models must always be registered with a FlatIndexConfig. After data has been ingested, call rebuild_index (or the rebuild-index CLI command) to switch to an HNSW index. Registering directly with HNSWIndexConfig raises a ValueError.

FLAT

Sequential scan — no index structure is built. Every query compares the query vector against all stored embeddings. Always correct, requires no build step.

from omop_emb.backends.index_config import FlatIndexConfig

FlatIndexConfig()  # no parameters

Use FLAT when the corpus is small (tens of thousands of concepts) or when exact results are required.

HNSW

Hierarchical Navigable Small World graph. Approximate nearest-neighbour search with sub-linear query time. Supported by pgvector only; not supported by sqlite-vec.

Parameter Default Effect
num_neighbors 32 Graph connectivity (M). Higher = better recall, larger index.
ef_construction 64 Build quality. Higher = better recall at build time, slower build.
ef_search 16 Query recall. Higher = better recall at query time, slower query.
from omop_emb.backends.index_config import HNSWIndexConfig
from omop_emb.config import MetricType

HNSWIndexConfig(
    metric_type=MetricType.COSINE,  # locked in at build time for HNSW
    num_neighbors=32,
    ef_construction=64,
    ef_search=16,
)

The HNSW workflow is always: register (FLAT) → ingest → rebuild index:

# 1. Register with FLAT — always
backend.register_model(model_name=..., provider_type=...,
                        index_config=FlatIndexConfig(), dimensions=768)

# 2. Ingest data
backend.upsert_embeddings(...)

# 3. Build HNSW index
backend.rebuild_index(model_name=...,
                       index_config=HNSWIndexConfig(metric_type=MetricType.COSINE))

Or via the CLI:

omop-emb embeddings add-embeddings --api-base ... --api-key ... --model nomic-embed-text
omop-emb maintenance rebuild-index --model nomic-embed-text --index-type hnsw --metric-type cosine

pgvector HNSW

HNSW is a SQL CREATE INDEX USING hnsw object built by rebuild_index. Without it, pgvector falls back to a sequential scan automatically. ef_search is applied per session at query time.

pgvector dimension limit

The pgvector vector column type supports at most 2,000 dimensions. Models with more than 2,000 dimensions automatically use the halfvec column type (up to 4,000 dimensions). Registering above 4,000 dimensions raises a ValueError.

Metrics

Each backend supports a subset of distance metrics:

Backend FLAT HNSW
sqlite-vec L2, Cosine, L1
pgvector L2, Cosine, L1 L2, Cosine, L1
FAISS sidecar L2, Cosine L2, Cosine

For FLAT models, the metric is supplied by the caller at query time. For HNSW models, the metric is locked in at rebuild_index time and must be supplied consistently at every query.