Files
lancedb/docs/web/indexing/scalar-index.mdx
Jack YeandBrendan Clement 47b7babc08 docs: bring the open-source documentation into this repository (#4122)
Move the public website pages into `docs/web` beside the SDK, with
shared navigation and executable example sources in `docs/web-tests`.
The combined website is assembled by
[lancedb/docs#355](https://github.com/lancedb/docs/pull/355), with six
Enterprise replacements from
[lancedb/sophon#7681](https://github.com/lancedb/sophon/pull/7681).

This includes Function guidance, runnable Quickstarts, regenerated
snippets, production contact/demo fixes, the legacy SQL route, and the
LeRobot redirect. LeRobot remains attributed to Geneva; all 34 Geneva
pages remain in the docs repository until Function officially launches.

Materialized-view guidance follows the full rebuild refresh introduced
by #4373: local refresh writes the complete result, has no `full`
argument, supports `source_version`, and no longer requires stable row
IDs. Deployment SQL refresh options and stable-ID setup are qualified by
version/configuration. Function input guidance distinguishes ordinary
stored columns from computed inputs supported by newer deployments.

**Validation:** independently accepted local checks include 95 Python
tests (real Enterprise excluded), eight TypeScript tests, Rust
Quickstart, snippet parity, typos, deterministic assembly, Mint
validation and links/anchors/redirects. The combined baseline retains
all 181 production routes and 17 legacy redirects. Examples ran locally
against source-built Python SDK 0.40.0b13; existing GitHub workflows do
not run `docs/web-tests`. New main at `069b2f7d` is 0.41.0b0 and adds
computed Function inputs; that SDK was not exercised by the accepted
tests. Latest prose corrections receive focused site validation.

**Merge/rollout:** merge this and #7681 before #355's combined CI, which
reads producer `main`. This is migration infrastructure: section
overlays, source-owned skills and automatic versioned paired releases
remain follow-ups. The current manual publisher is transitional. Hosted
validation, a supported SDK/Enterprise release pair and a real
Enterprise walkthrough remain pending.

Production Mintlify watches `lancedb/docs:deploy-freeze`, directory
`docs`; preserve that source through review. The separate SDK GitHub
Pages reference workflow rebuilds on main merges.

**Updated-head CI (9a50f8db):**
[Typos](https://github.com/lancedb/lancedb/actions/runs/37654185443/job/112904990558),
[PR
title](https://github.com/lancedb/lancedb/actions/runs/37654179507/job/112904974595)
and
[Aikido](https://app.aikido.dev/featurebranch/scan/215386145?groupId=74229)
pass. Critical findings in the example lockfiles were patched: all Sharp
copies use 0.35.5, protobufjs 7.6.6, tar 7.5.22, quinn-proto 0.11.19,
zerovec 0.11.8 and zerovec-derive 0.11.6. The SDK's product dependencies
are unchanged. The revised lockfiles pass eight TypeScript examples,
HuggingFace decode/resize/crop/rescale/normalize plus AVIF roundtrip,
and Rust Quickstart. Aikido passing does not mean every advisory is
cleared; npm still reports 29 high and five moderate advisories in the
example dependency graph. No scanner bypass or suppression was added.

The Function prose correction passes combined/standalone Mint,
deterministic assembly, routes and assets. The revised 325-file site
checksum is
`175bec06864abb400dd292e667ba93448c0479b8359fd7bf6146afabf9040647`. This
PR remains open for independent review; further merges are on hold.

---------

Co-authored-by: Brendan Clement <brendan.clement@lancedb.com>
2026-10-07 11:15:00 -07:00

254 lines
9.5 KiB
Plaintext

---
title: "Scalar Indexes"
sidebarTitle: "Scalar Index"
description: "Learn how to use scalar indexes in LanceDB for efficient metadata filtering and query optimization."
icon: "tree"
---
import {
PyScalarIndexBuild as ScalarIndexBuild,
PyScalarIndexWait as ScalarIndexWait,
PyScalarIndexOptimize as ScalarIndexOptimize,
PyScalarIndexFilter as ScalarIndexFilter,
PyScalarIndexPrefilter as ScalarIndexPrefilter,
PyScalarIndexUuidType as ScalarIndexUuidType,
PyScalarIndexUuidData as ScalarIndexUuidData,
PyScalarIndexUuidTable as ScalarIndexUuidTable,
PyScalarIndexUuidWait as ScalarIndexUuidWait,
PyScalarIndexUuidUpsert as ScalarIndexUuidUpsert,
PyScalarIndexNestedFields as ScalarIndexNestedFields,
} from '/snippets/indexing.mdx';
Scalar indexes organize data by scalar attributes (e.g., numbers, categories) and enable fast filtering of vector data. They accelerate retrieval of scalar data associated with vectors, thus enhancing query performance.
LanceDB supports four types of scalar indexes:
- `BTREE`: Stores column data in sorted order for binary search. Best for columns with many unique values.
- `BITMAP`: Uses bitmaps to track value presence. Ideal for columns with few unique values (e.g., categories, tags).
- `LABEL_LIST`: Special index for `List<T>` and `LargeList<T>` columns of primitive values supporting `array_contains_all` and `array_contains_any` queries.
- `FM`: FM-Index over string or binary columns that accelerates substring search via `contains(col, 'needle')`.
<Tip title="Working with scalar indices">
Scalar indices serve as a foundational optimization layer, accelerating filtering across diverse search workloads. They can be combined with:
- Vector search (prefilter results using metadata)
- Full-text search (combining keyword matching with structured filters)
- SQL scans (optimizing WHERE clauses on scalar columns)
- Key-value lookups (enabling rapid primary key-based retrievals)
</Tip>
## Choosing the Right Index Type
| Data Type | Filter | Index Type |
|:----------------------------------------------------------------|:------------------------------------------|:-------------|
| Numeric, String, Temporal | `<`, `=`, `>`, `in`, `between`, `is null` | `BTREE` |
| Boolean, numbers or strings with fewer than 1,000 unique values | `<`, `=`, `>`, `in`, `between`, `is null` | `BITMAP` |
| List of low cardinality of numbers or strings | `array_has_any`, `array_has_all` | `LABEL_LIST` |
| String or binary (`Utf8`, `LargeUtf8`, `Binary`, `LargeBinary`) | `contains(col, 'needle')` | `FM` |
## Scalar Index Operations
### 1. Build the Index
You can create multiple scalar indexes within a table. By default, the index will be `BTREE`, but you can always configure another type like `BITMAP`
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexBuild}
</CodeBlock>
</CodeGroup>
<Note title="LanceDB Enterprise">
If you are using LanceDB Enterprise, the `create_scalar_index` API returns immediately, but the building of the scalar index is asynchronous. To wait until all data is fully indexed, you can specify the `wait_timeout` parameter on `create_scalar_index()` or call `wait_for_index()` on the table.
</Note>
### 2. Check Index Status
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexWait}
</CodeBlock>
</CodeGroup>
`wait_for_index(...)` waits until the named scalar indexes exist and `index_stats(...)` reports `num_unindexed_rows == 0`. If a table is receiving steady writes, that fully indexed state may not stabilize before the timeout.
### 3. Update the Index
Updating the table data (adding, deleting, or modifying records) requires that you also update the scalar index. This can be done by calling `optimize`, which will trigger an update to the existing scalar index.
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexOptimize}
</CodeBlock>
</CodeGroup>
<Note title="LanceDB Enterprise">
New data added after creating the scalar index will still appear in search results if optimize is not used, but with increased latency due to a flat search on the unindexed portion. LanceDB Enterprise automates the optimize process, minimizing the impact on search speed.
</Note>
### 4. Run Indexed Searches
The following scan will be faster if the column `book_id` has a scalar index:
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexFilter}
</CodeBlock>
</CodeGroup>
Scalar indexes can also speed up scans containing a vector search or full text search, and a prefilter:
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexPrefilter}
</CodeBlock>
</CodeGroup>
## Indexing nested fields
Scalar indexes can target a scalar field inside a struct by passing its full dotted path. The path is preserved end to end: it's the value you pass to `create_scalar_index`, it's what `list_indices()` reports under `columns`, and it's the column reference you use in filter predicates.
```python
# Schema: pa.struct([pa.field("user_id", pa.int32())]) stored under the `metadata` column.
table.create_scalar_index("metadata.user_id", name="metadata_user_id_idx")
# The same dotted path works in WHERE clauses.
table.search().where("metadata.user_id = 42").limit(1).to_list()
```
<Note>
Nested paths follow Lance field-path semantics: dot-separate each struct field from root to leaf (for example, `metadata.author.name`). The same convention applies to FTS and vector indexes.
</Note>
## FM-Index for substring search
The `FM` index is a scalar index built over string or binary columns that
accelerates substring lookups expressed as `contains(col, 'needle')`. Unlike the
tokenized [FTS index](/indexing/fts-index), which matches whole words after
tokenization, the FM-Index matches arbitrary substrings of the raw bytes — so it
works well for URLs, file paths, identifiers, log lines, or any column where you
search for a fragment rather than a word.
Use the FM-Index when:
- Filters use `contains(col, 'needle')` (substring), not equality or word search.
- The column is `Utf8`, `LargeUtf8`, `Binary`, or `LargeBinary`.
- You want substring matches without paying for tokenization, language analysis,
or BM25 scoring.
Pick `FTS` instead when you need word-level relevance ranking, phrase queries,
or language-aware tokenization.
### Create an FM-Index
Build an FM-Index with the async `create_index` API by passing the `Fm` config in
Python or `Index.fm()` in TypeScript. In Rust, use `Index::Fm(FmIndexBuilder::default())`.
<CodeGroup>
```python Python icon="python"
from lancedb.index import Fm
await tbl.create_index("text", config=Fm())
```
```typescript TypeScript icon="square-js"
import * as lancedb from "@lancedb/lancedb";
import { Index } from "@lancedb/lancedb";
await tbl.createIndex("text", { config: Index.fm() });
```
```rust Rust icon="rust"
use lancedb::index::Index;
use lancedb::index::scalar::FmIndexBuilder;
tbl.create_index(&["text"], Index::Fm(FmIndexBuilder::default()))
.execute()
.await?;
```
</CodeGroup>
After the index is built, substring filters use it automatically:
```python
table.search().where("contains(text, 'needle')").limit(10).to_pandas()
```
`list_indices()` reports the index type as `"Fm"`.
## Index UUID Columns
LanceDB supports scalar indexes on UUID columns (stored as `FixedSizeBinary(16)`), enabling efficient lookups and filtering on UUID-based primary keys.
<Note>
**To use `FixedSizeBinary`, ensure you have:**
- Python SDK version `0.22.0` or later
- TypeScript SDK version `0.19.0` or later
</Note>
### 1. Define UUID Type
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexUuidType}
</CodeBlock>
</CodeGroup>
### 2. Generate UUID Data
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexUuidData}
</CodeBlock>
</CodeGroup>
### 3. Create Table with UUID Column
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexUuidTable}
</CodeBlock>
</CodeGroup>
### 4. Create and Wait for the Index
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexUuidWait}
</CodeBlock>
</CodeGroup>
### 5. Perform Operations with the UUID Index
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexUuidUpsert}
</CodeBlock>
</CodeGroup>
## Index nested fields
You can build a scalar index on a field inside a struct column by passing the
canonical dot-separated path to `create_index`. This is useful when filters
target attributes nested under a `metadata`-style column, for example
`metadata.user_id` or `metadata.event.type`.
If a literal segment of the path itself contains a dot (for example a column
named `user.id` nested inside `metadata`), wrap that segment in backticks so
LanceDB can tell the dot apart from the path separator: `` metadata.`user.id` ``.
`list_indices()` echoes the same canonical path back, so the column you pass in
round-trips through index metadata regardless of nesting depth or escaping.
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{ScalarIndexNestedFields}
</CodeBlock>
</CodeGroup>
<Note>
Composite indexes that cover multiple columns aren't supported yet. Each
`create_index` call must target a single (possibly nested) field path.
</Note>