IVF + HNSWIn LanceDB, HNSW is not exposed as a top-level vector index. Instead, it’s available as a sub-index inside IVF partitions. What this means in practice is that vectors are first partitioned by IVF, then each selected partition is searched using an HNSW graph. LanceDB supports the unquantized variant
IVF_HNSW_FLAT, along with quantized variants such as IVF_HNSW_PQ and IVF_HNSW_SQ. This combines IVF’s scalability with HNSW’s higher-recall ANN search within partitions.
Compression ratios are practical rules of thumb and can vary with vector distribution, metric, and configuration.
For small dimensions, choose
IVF_PQ for accuracy, not for guaranteed higher compression than IVF_RQ.
Index Tuning
Start with these values, then tune for your workload:- HNSW-backed IVF indexes (
IVF_HNSW_FLAT,IVF_HNSW_SQ,IVF_HNSW_PQ)num_partitions: start atnum_rows // 1,048,576(rounded to an integer)- Lower
num_partitionscan reduce search latency, but index build may become slower because partitions are larger. ef_construction: start at150; increase for better recall, decrease for faster indexing.
IVF_RQnum_partitions: start atnum_rows // 4096(rounded to an integer). This is a strong default for most datasets.
IVF_PQnum_partitions: start atnum_rows // 4096(rounded to an integer).num_sub_vectors: start atdimension // 8. Increase for better recall, decrease for faster search and smaller indexes.- For small dimensions (
dimension <= 256),IVF_PQis often preferred overIVF_RQfor better accuracy at similar query performance.
Operational checksFor vector indexes, make sure to use the same distance metric when creating and querying the index. After appends or other writes, use
optimize() to fold new rows into existing indexes, then check index_stats(...) or wait_for_index(...) to confirm that the index has caught up.
wait_for_index(...) waits until the named indexes exist and report num_unindexed_rows == 0, and can time out if writes keep adding unindexed rows.Unless specified otherwise, vector indexing defaults to IVF_PQ, and scalar index creation defaults to
BTree. BTree and Bitmap indexes target scalar columns, not list columns; use LabelList for list containment filters.Understanding Vector Indexes
An ANN (Approximate Nearest Neighbors) index is a data structure that quickly produces an approximate solution to the -Nearest Neighbors (kNN) problem. It greatly improves upon the runtime of a brute-force kNN search, while admitting a slight decrease in accuracy. LanceDB uses the disk-based indexing technique IVF-PQ, discussed below. LanceDB differs from other vector databases in that it is built on top of Lance, an open-source columnar data format designed for performant ML workloads and fast random access. Due to the design of Lance, LanceDB’s indexing philosophy adopts a primarily disk-based indexing philosophy.Inverted File Index (IVF) and IVF-PQ
LanceDB uses IVF-PQ indexing, which combines the clustering-based Inverted File Index (IVF) with Product Quantization (PQ) to efficiently compress embeddings. We primarily discuss the IVF indexing technique here. An IVF index facilitates rapid nearest neighbor searches by drastically reducing the search space. Given a large set of stored vectors, the algorithm to produce an IVF index first computes a set of centroids corresponding to an approximate solution to the -means clustering problem. The centroids are then used to partition the set of vectors as follows: each vector is assigned to the centroid nearest to it in the (or user-specified) metric. The set of vectors assigned to a centroid is called its cluster. This data is then recorded as an index which identifies each centroid with its cluster. The following image shows a -dimensional Euclidean space partitioned according to this algorithm. The colored marks denote centroids.
nprobe parameter, which controls the number of Voronoi cells to search during a query. The higher the nprobe, the more accurate the results, but the slower the query.

Hierarchical Navigable Small World (HNSW)
Approximate Nearest Neighbor (ANN) search is a method for finding data points near a given point in a dataset, though not always the exact nearest one. HNSW is one of the most accurate and fastest Approximate Nearest Neighbour search algorithms, It’s beneficial in high-dimensional spaces where finding the same nearest neighbor would be too slow and costly.Types of ANN Search Algorithms
Approximate Nearest Neighbor (ANN) search is a method for finding data points near a given point in a dataset, though not always the exact nearest one. For example, HNSW is an ANN index that performs well in high-dimensional spaces where other techniques prove too slow and costly. There are three main types of ANN search algorithms:- Tree-based search algorithms: Use a tree structure to organize and store data points.
- Hash-based search algorithms: Use a specialized geometric hash table to store and manage data points. These algorithms typically focus on theoretical guarantees, and don’t usually perform as well as the other approaches in practice.
- Graph-based search algorithms: Use a graph structure to store data points, which can be a bit complex.
HNSW also combines this with the ideas behind a classic 1-dimensional search data structure: the skip list.
Understanding -Nearest Neighbor Graphs
The -nearest neighbor graph actually predates its use for ANN search. Its construction is quite simple:- Each vector in the dataset is given an associated vertex.
- Each vertex has outgoing edges to its k nearest neighbors. That is, the k closest other vertices by Euclidean distance between the two corresponding vectors. This can be thought of as a “friend list” for the vertex.
- For some applications (including nearest-neighbor search), the incoming edges are also added.
- Given a query vector, start at some fixed “entry point” vertex (e.g. the approximate center node).
- Look at that vertex’s neighbors. If any of them are closer to the query vector than the current vertex, then move to that vertex.
- Repeat until a local optimum is found.
In fact, another data structure is not needed: This can be done “incrementally”. That is, if you start with a k-ANN graph for n-1 vertices, you can extend it to a k-ANN graph for n vertices as well by using the graph to obtain the k-ANN for the new vertex. One downside of k-NN and k-ANN graphs alone is that one must typically build them with a large value of k to get decent results, resulting in a large index.
Hierarchical Navigable Small Worlds (HNSW)
HNSW builds on k-ANN in two main ways:- Instead of getting the k-approximate nearest neighbors for a large value of k, it sparsifies the k-ANN graph using a carefully chosen “edge pruning” heuristic, allowing for the number of edges per vertex to be limited to a relatively small constant.
- The “entry point” vertex is chosen dynamically using a recursively constructed data structure on a subset of the data, similarly to a skip list.
- At the bottom-most layer, a k-ANN graph on the whole dataset is present.
- At the second layer, a k-ANN graph on a fraction of the dataset (e.g. 10%) is present.
- At the Lth layer, a k-ANN graph is present. It is over a (constant) fraction (e.g. 10%) of the vectors/vertices present in the L-1th layer.
- At the top layer (using an arbitrary vertex as an entry point), use the greedy local search routine on the k-ANN graph to get an approximate nearest neighbor at that layer.
- Using the approximate nearest neighbor found in the previous layer as an entry point, find an approximate nearest neighbor in the next layer with the same method.
- Repeat until the bottom-most layer is reached. Then use the entry point to find multiple nearest neighbors (e.g. top 10).
Using Vector Indexes
Manual Indexing
If using LanceDB OSS, you will have to create the vector index manually, by callingtable.create_index(), and updating the index as new data arrives and tuning its parameters is also a manual process.
Automatic Indexing
Enterprise-only Vector indexing is managed automatically in LanceDB Enterprise. When a table is created in LanceDB Enterprise, the system asynchronously updates and optimizes the index as a background process:- Infers vector columns from the schema
- Optimizes the
IVF_PQindex without manual configuration - Automatically manages indexing parameters
l2 (the Euclidean norm).
You can call
create_index() with different parameters to create a new index — this replaces any existing index.
Although the create_index API returns immediately, the building of the vector index is asynchronous. To wait until all data is fully indexed, you can specify the wait_timeout parameter.wait_for_index(...) waits for the named index to exist and for index_stats(...) to report num_unindexed_rows == 0; it can time out if new writes keep arriving.
Rows appended after an index build remain outside that index until optimization refreshes it. Normal
search still checks those unindexed rows with a slower fallback path; fast_search() skips that
fallback and searches only indexed rows.
Example: Construct an IVF Index
In this example, we will create an index for a table containing 1536-dimensional vectors. The index will use IVF_PQ with L2 distance, which is well-suited for high-dimensional vector search. Make sure you have enough data in your table (at least a few thousand rows) for effective index training.Index Configuration
Sometimes you need to configure the index beyond default parameters:- Index Types:
IVF_HNSW_FLAT: highest recall, with no vector quantizationIVF_HNSW_SQ: best recall/latency trade-offIVF_RQ: best compression for large, high-dimensional datasetsIVF_PQ: often higher accuracy thanIVF_RQfor small dimensions (<= 256) at similar query performance
metrics: default isl2, other available arecosineordot- When using
cosinesimilarity, distances range from 0 (identical vectors) to 2 (maximally dissimilar)
- When using
num_partitions: use index-specific starting points from the section above:- HNSW-backed IVF indexes (
IVF_HNSW_FLAT,IVF_HNSW_SQ,IVF_HNSW_PQ):num_rows // 1,048,576 IVF_RQandIVF_PQ:num_rows // 4096
- HNSW-backed IVF indexes (
target_partition_size: alternative IVF sizing knob that asks LanceDB to derive the partition count from a target number of rows per partition. If you set bothnum_partitionsandtarget_partition_size,num_partitionstakes precedence.num_sub_vectors: applies toIVF_PQ; start withdimension // 8. Larger values often improve recall but can slow search.
1. Setup
Connect to LanceDB and open the table you want to index.2. Construct an IVF Index
Create anIVF_PQ index with cosine similarity. Specify vector_column_name if you use multiple vector columns or non-default names. For a vector field nested inside a struct, use dot notation (e.g. image.embedding); see Selecting the vector column for the full syntax. You can switch index_type to IVF_RQ, IVF_HNSW_SQ, or IVF_HNSW_FLAT depending on your recall/latency/compression target.
Indexing nested vector fields
If your vector column lives inside a struct, pass its full dotted path asvector_column_name. The same path is used at query time and is what list_indices() reports under columns:
Nested paths follow Lance field-path semantics: dot-separate each struct field from root to leaf (for example,
image.thumbnail.embedding). The same convention applies to FTS and scalar indexes.Async API and Config Objects
With asynchronous Python connections, create vector indexes withawait table.create_index("vector", config=...). The config object carries the same index choices you configure in the synchronous API, such as distance metric, partition count, and quantization settings:
Use these Python config classes for the index types shown on this page:
3. Query the IVF Index
Search using a random 1,536-dimensional embedding.Search Configuration
Core knobs available on a vector search call:Filtered queries and adaptive nprobes. When a
where(...) filter is active, LanceDB starts by scanning minimum_nprobes partitions and only extends toward maximum_nprobes if fewer than limit rows survive the filter. Setting minimum_nprobes == maximum_nprobes (or calling nprobes(n)) disables this adaptive behavior and fixes the partition count.nprobes behavior by index type:
Advanced Search Controls
These controls are useful for thresholded retrieval, recall measurement, and working around index-level metric constraints.
Thresholding with
distance_range:
Measuring recall with bypass_vector_index:
Compare ANN results against a flat-scan ground truth to compute recall@k. This is the standard way to pick nprobes for your workload.
Multivector indexing currently requires
distance_type="cosine" — l2 is rejected at index-creation time. That restriction is why bypass_vector_index() is the escape hatch for non-cosine queries on a multivector column: the metric you want at query time cannot be served by the index, so you fall back to a flat scan. See Multivector Search for the full rules.Example: Construct an HNSW Index
Index Configuration
There are four key parameters to set when constructing an HNSW index:index_type: chooseIVF_HNSW_SQfor a strong recall/latency/size trade-off, orIVF_HNSW_FLATwhen you want the IVF+HNSW structure without vector quantization.metric: The default isl2euclidean distance metric. Other available aredotandcosine.m: The number of neighbors to select for each vector in the HNSW graph.ef_construction: The number of candidates to evaluate during the construction of the HNSW graph.
1. Construct an HNSW Index
The snippet below usesIVF_HNSW_SQ. If you want the unquantized variant, change index_type to IVF_HNSW_FLAT.
2. Query the HNSW Index
Example: Construct a Binary Vector Index
Binary vectors are useful for hash-based retrieval, fingerprinting, or any scenario where data can be represented as bits.Index Configuration
- Store binary vectors as fixed-size binary data (uint8 arrays, with 8 bits per byte). For storage, pack binary vectors into bytes to save space.
- Index Type:
IVF_FLATis used for indexing binary vectors metric: thehammingdistance is used for similarity search- The dimension of binary vectors must be a multiple of 8. For example, a 128-dimensional vector is stored as a uint8 array of size 16.
1. Create Table and Schema
2. Generate and Add Data
3. Construct the Binary Index
4. Vector Search
Check Index Status
Vector index creation runs in the background and may take some time to complete. While it is ongoing, you can check its status either programmatically through the API or from the LanceDB Enterprise UI. In the LanceDB Enterprise UI, navigate to your table page - the “Index” column reflects each column’s index status: it is blank when no index exists, shows an “in progress” label while the index is being built, and shows the index type once the build completes. Programmatically, uselist_indices() and index_stats(). By default, the index name is formed by appending _idx to the column name (e.g., a keywords_embeddings column produces keywords_embeddings_idx). Note that list_indices() only returns information after the index is fully built.
To wait until all data is fully indexed, you can specify the wait_timeout parameter on create_index() or call wait_for_index() on the table.
Each entry returned by list_indices() also carries detailed per-index metadata, so you can inspect an index without a follow-up index_stats() call. Node.js exposes the same fields in camelCase (num_indexed_rows → numIndexedRows):
These fields are populated for local and embedded tables. On LanceDB Enterprise remote tables they are returned as
None / undefined until the server response surfaces them.Custom Index Names
The{column}_idx suffix is a default convention, not the only supported naming path. Pass name=... to create_index() to override it — useful when you want to manage multiple indexes on the same column (for example, side-by-side IVF_PQ and IVF_HNSW_SQ builds) or when you script index replacement by name. Once set, list_indices(), index_stats(name), and wait_for_index([name]) all reference the custom name.