mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-27 14:06:14 +00:00
1529 lines
55 KiB
Plaintext
1529 lines
55 KiB
Plaintext
---
|
|
title: "Tables"
|
|
sidebarTitle: "Table"
|
|
description: "The object most operations act on: creating, opening, renaming, cloning and dropping one."
|
|
icon: table
|
|
keywords: ["table", "create_table", "open_table", "clone", "rename", "drop"]
|
|
availability:
|
|
oss: available
|
|
enterprise: available
|
|
summary: >-
|
|
Tables are created, evolved, versioned and dropped in the embedded library
|
|
and in LanceDB Enterprise. Renaming, cloning, dropping every table and pruning old
|
|
versions differ between the two.
|
|
---
|
|
|
|
import {
|
|
PySchemaAddSetup,
|
|
TsSchemaAddSetup as TsSchemaAddSetup,
|
|
RsSchemaAddSetup as RsSchemaAddSetup,
|
|
PyAddColumnsDefaultValues as AddColumnsDefaultValues,
|
|
TsAddColumnsDefaultValues as TsAddColumnsDefaultValues,
|
|
RsAddColumnsDefaultValues as RsAddColumnsDefaultValues,
|
|
PyAddColumnsNullable as AddColumnsNullable,
|
|
TsAddColumnsNullable as TsAddColumnsNullable,
|
|
RsAddColumnsNullable as RsAddColumnsNullable,
|
|
PySchemaAlterSetup as SchemaAlterSetup,
|
|
TsSchemaAlterSetup as TsSchemaAlterSetup,
|
|
RsSchemaAlterSetup as RsSchemaAlterSetup,
|
|
PyAlterColumnsRename as AlterColumnsRename,
|
|
TsAlterColumnsRename as TsAlterColumnsRename,
|
|
RsAlterColumnsRename as RsAlterColumnsRename,
|
|
PyAlterColumnsDataType as AlterColumnsDataType,
|
|
TsAlterColumnsDataType as TsAlterColumnsDataType,
|
|
RsAlterColumnsDataType as RsAlterColumnsDataType,
|
|
PyAlterColumnsNullable as AlterColumnsNullable,
|
|
TsAlterColumnsNullable as TsAlterColumnsNullable,
|
|
RsAlterColumnsNullable as RsAlterColumnsNullable,
|
|
PyAlterColumnsMultiple as AlterColumnsMultiple,
|
|
TsAlterColumnsMultiple as TsAlterColumnsMultiple,
|
|
PyAlterColumnsWithExpression as AlterColumnsWithExpression,
|
|
TsAlterColumnsWithExpression as TsAlterColumnsWithExpression,
|
|
RsAlterColumnsMultiple as RsAlterColumnsMultiple,
|
|
RsAlterColumnsWithExpression as RsAlterColumnsWithExpression,
|
|
PySchemaDropSetup as SchemaDropSetup,
|
|
TsSchemaDropSetup as TsSchemaDropSetup,
|
|
RsSchemaDropSetup as RsSchemaDropSetup,
|
|
PyDropColumnsSingle as DropColumnsSingle,
|
|
TsDropColumnsSingle as TsDropColumnsSingle,
|
|
RsDropColumnsSingle as RsDropColumnsSingle,
|
|
PyDropColumnsMultiple as DropColumnsMultiple,
|
|
TsDropColumnsMultiple as TsDropColumnsMultiple,
|
|
RsDropColumnsMultiple as RsDropColumnsMultiple,
|
|
PyAlterVectorColumn as AlterVectorColumn,
|
|
TsAlterVectorColumn as TsAlterVectorColumn,
|
|
RsAlterVectorColumn as RsAlterVectorColumn,
|
|
PySchemaFieldMetadataMerge as SchemaFieldMetadataMerge,
|
|
TsSchemaFieldMetadataMerge as TsSchemaFieldMetadataMerge,
|
|
RsSchemaFieldMetadataMerge as RsSchemaFieldMetadataMerge,
|
|
PySchemaFieldMetadataReplace as SchemaFieldMetadataReplace,
|
|
TsSchemaFieldMetadataReplace as TsSchemaFieldMetadataReplace,
|
|
RsSchemaFieldMetadataReplace as RsSchemaFieldMetadataReplace,
|
|
PyVersioningBasicSetup as VersioningBasicSetup,
|
|
TsVersioningBasicSetup as TsVersioningBasicSetup,
|
|
RsVersioningBasicSetup as RsVersioningBasicSetup,
|
|
PyVersioningCheckInitialVersion as VersioningCheckInitialVersion,
|
|
TsVersioningCheckInitialVersion as TsVersioningCheckInitialVersion,
|
|
RsVersioningCheckInitialVersion as RsVersioningCheckInitialVersion,
|
|
PyVersioningUpdateData as VersioningUpdateData,
|
|
TsVersioningUpdateData as TsVersioningUpdateData,
|
|
RsVersioningUpdateData as RsVersioningUpdateData,
|
|
PyVersioningAddData as VersioningAddData,
|
|
TsVersioningAddData as TsVersioningAddData,
|
|
RsVersioningAddData as RsVersioningAddData,
|
|
PyVersioningCheckVersionsAfterMod as VersioningCheckVersionsAfterMod,
|
|
TsVersioningCheckVersionsAfterMod as TsVersioningCheckVersionsAfterMod,
|
|
RsVersioningCheckVersionsAfterMod as RsVersioningCheckVersionsAfterMod,
|
|
PyVersioningListAllVersions as VersioningListAllVersions,
|
|
TsVersioningListAllVersions as TsVersioningListAllVersions,
|
|
RsVersioningListAllVersions as RsVersioningListAllVersions,
|
|
PyVersioningRollback as VersioningRollback,
|
|
TsVersioningRollback as TsVersioningRollback,
|
|
RsVersioningRollback as RsVersioningRollback,
|
|
PyVersioningCheckoutLatest as VersioningCheckoutLatest,
|
|
TsVersioningCheckoutLatest as TsVersioningCheckoutLatest,
|
|
RsVersioningCheckoutLatest as RsVersioningCheckoutLatest,
|
|
PyVersioningDeleteData as VersioningDeleteData,
|
|
TsVersioningDeleteData as TsVersioningDeleteData,
|
|
RsVersioningDeleteData as RsVersioningDeleteData,
|
|
PyVersioningTags as VersioningTags,
|
|
TsVersioningTags as TsVersioningTags,
|
|
RsVersioningTags as RsVersioningTags,
|
|
RsVersioningMakeQuotesReader as RsVersioningMakeQuotesReader,
|
|
} from '/snippets/tables.mdx';
|
|
import {
|
|
PyMultimodalImports as MultimodalImports,
|
|
TsMultimodalImports as TsMultimodalImports,
|
|
RsMultimodalImports as RsMultimodalImports,
|
|
PyCreateDummyData as CreateDummyData,
|
|
TsCreateDummyData as TsCreateDummyData,
|
|
RsCreateDummyData as RsCreateDummyData,
|
|
PyDefineSchema as DefineSchema,
|
|
TsDefineSchema as TsDefineSchema,
|
|
RsDefineSchema as RsDefineSchema,
|
|
PyIngestData as IngestData,
|
|
TsIngestData as TsIngestData,
|
|
RsIngestData as RsIngestData,
|
|
PySearchData as SearchData,
|
|
TsSearchData as TsSearchData,
|
|
RsSearchData as RsSearchData,
|
|
PyProcessResults as ProcessResults,
|
|
TsProcessResults as TsProcessResults,
|
|
RsProcessResults as RsProcessResults,
|
|
PyBlobApiSetup as BlobApiSetup,
|
|
TsBlobApiSetup as TsBlobApiSetup,
|
|
RsBlobApiImports as RsBlobApiImports,
|
|
RsBlobApiSetup as RsBlobApiSetup,
|
|
PyBlobApiSchema as BlobApiSchema,
|
|
TsBlobApiSchema as TsBlobApiSchema,
|
|
RsBlobApiSchema as RsBlobApiSchema,
|
|
PyBlobApiIngest as BlobApiIngest,
|
|
PyBlobApiBytes as BlobApiBytes,
|
|
TsBlobApiIngest as TsBlobApiIngest,
|
|
RsBlobApiIngest as RsBlobApiIngest,
|
|
PyBlobApiFiles as BlobApiFiles,
|
|
TsBlobApiFiles as TsBlobApiFiles,
|
|
RsBlobApiFiles as RsBlobApiFiles,
|
|
PyBlobApiFetch as BlobApiFetch,
|
|
TsBlobApiFetch as TsBlobApiFetch,
|
|
RsBlobApiFetch as RsBlobApiFetch,
|
|
PyBlobApiRanges as BlobApiRanges,
|
|
TsBlobApiRanges as TsBlobApiRanges,
|
|
RsBlobApiRanges as RsBlobApiRanges,
|
|
TsBlobApiBytesMode as TsBlobApiBytesMode,
|
|
PyLegacyBlobApiSchema as LegacyBlobApiSchema,
|
|
TsLegacyBlobApiSchema as TsLegacyBlobApiSchema,
|
|
RsLegacyBlobApiSchema as RsLegacyBlobApiSchema,
|
|
PyLegacyBlobApiIngest as LegacyBlobApiIngest,
|
|
TsLegacyBlobApiIngest as TsLegacyBlobApiIngest,
|
|
RsLegacyBlobApiIngest as RsLegacyBlobApiIngest,
|
|
PyLegacyBlobApiToPandas as LegacyBlobApiToPandas,
|
|
PyLegacyQueryToPandasKwargs as LegacyQueryToPandasKwargs,
|
|
} from '/snippets/multimodal.mdx';
|
|
import {
|
|
PyBlobApiToPandas as BlobApiToPandas,
|
|
PyQueryToPandasKwargs as QueryToPandasKwargs,
|
|
} from '/snippets/blob_pylance.mdx';
|
|
|
|
A table stores data in rows and columns and keeps a history of committed
|
|
versions. Queries read table data, writes update it, and indexes accelerate
|
|
searches over its columns. Branches let you make changes separately from the
|
|
main table history.
|
|
|
|
This page covers table management, data types, schema changes and version
|
|
history. For loading records, see [Ingestion](/ingestion). For changes to
|
|
columns, see [Schema evolution](/management/table#schema).
|
|
|
|
<Note>
|
|
In LanceDB Enterprise, tables are also created and dropped in SQL with [`CREATE TABLE` and `DROP TABLE`](/sql/ddl/table).
|
|
</Note>
|
|
|
|
## Table structure and metadata {#carries}
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| **Columns** | including vectors, blobs, and [computed](/feature-engineering/add-computed-column) columns |
|
|
| **Versions** | every write commits one, so reads can [travel back](/management/table#versioning) |
|
|
| **Indexes** | vector, full-text and scalar, built over its columns |
|
|
| **Branches** | isolated lines of change you can [merge or discard](/tables/branching) |
|
|
|
|
## Create a table {#create}
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
tbl = db.create_table("documents", data)
|
|
tbl = db.create_table("documents", schema=schema) # empty, typed
|
|
```
|
|
|
|
```typescript TypeScript icon="square-js"
|
|
const tbl = await db.createTable("documents", data);
|
|
const tbl = await db.createEmptyTable("documents", schema);
|
|
```
|
|
|
|
```sql SQL
|
|
CREATE TABLE documents (id BIGINT, title VARCHAR)
|
|
```
|
|
</CodeGroup>
|
|
|
|
`data` can be a list of objects, an Arrow table or reader, a pandas or polars
|
|
frame, or Pydantic models. [Ingesting data](/ingestion) covers each, plus
|
|
`exist_ok` and `mode="overwrite"` for re-runnable scripts.
|
|
|
|
<Warning>
|
|
An explicit schema is where nullability is decided. In the embedded library,
|
|
creation is also the only point at which a table can be given **stable row
|
|
ids**; in LanceDB Enterprise, whether a new table gets them is the deployment's
|
|
setting. See [Limits](/basics/limits#schema).
|
|
</Warning>
|
|
|
|
## Open and list tables {#open}
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
tbl = db.open_table("documents")
|
|
db.table_names()
|
|
```
|
|
|
|
```typescript TypeScript icon="square-js"
|
|
const tbl = await db.openTable("documents");
|
|
await db.tableNames();
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
Each accepts `namespace_path` to act inside a [namespace](/management/namespace)
|
|
rather than at the database root.
|
|
|
|
To find out whether a table exists, open it and handle the error, or look for
|
|
its name in a listing. A missing table raises `ValueError` on a local directory
|
|
or a deployment, and `lance_namespace.TableNotFoundError` on a connection from
|
|
`connect_namespace`. A listing returns one page of names at a time, so check
|
|
every page. Python's `table_exists` is no substitute: a local directory or a
|
|
deployment raises `NotImplementedError` for it, and on a connection from
|
|
`connect_namespace("dir", ...)` or `connect_namespace("rest", ...)` it fails with
|
|
an `AttributeError`. TypeScript has no `tableExists`.
|
|
|
|
## Rename and clone tables {#rename-clone}
|
|
|
|
```python
|
|
db.rename_table("documents", "docs")
|
|
|
|
db.clone_table(
|
|
"docs_snapshot",
|
|
source_uri="s3://bucket/db/documents.lance",
|
|
source_version=42, # or source_tag="release-1"
|
|
is_shallow=True,
|
|
)
|
|
```
|
|
|
|
Renaming is not supported on a local directory connection. In LanceDB Enterprise,
|
|
whether a table can be renamed or cloned at all depends on how the deployment
|
|
stores its catalog.
|
|
|
|
A **shallow** clone (the default) references the source's data files rather
|
|
than copying them, so it is fast and cheap regardless of table size. It is the
|
|
practical way to branch an experiment off a known version, or to pin a training
|
|
set to the exact version a model saw.
|
|
|
|
<Warning>
|
|
A shallow clone depends on the source's files continuing to exist. If the source
|
|
is [compacted](/maintenance/compaction) past the retention window, the version
|
|
the clone points at can be pruned. Local OSS connections support shallow clones
|
|
only; `is_shallow=False` is not implemented. If the copy must outlive the
|
|
source's history, copy its data into an independent table instead. A deployment
|
|
that supports cloning also makes shallow clones only, and one whose catalog
|
|
does not support it refuses every clone.
|
|
</Warning>
|
|
|
|
<Note>
|
|
**Enterprise job type:** on an Enterprise cluster that drops tables in the background,
|
|
the teardown runs as a job: `drop_table`, which fences the table's directory
|
|
with a marker and deletes its content, or `drop_object`, depending on how the
|
|
deployment stores its catalog. See
|
|
[Background jobs](/maintenance/background-jobs#teardown).
|
|
</Note>
|
|
|
|
## Drop a table {#drop}
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
db.drop_table("documents")
|
|
job = db.drop_table_async("documents")
|
|
db.drop_all_tables()
|
|
```
|
|
|
|
```typescript TypeScript icon="square-js"
|
|
await db.dropTable("documents");
|
|
const job = await db.dropTableAsync("documents");
|
|
await db.dropAllTables();
|
|
```
|
|
|
|
```sql SQL
|
|
DROP TABLE documents
|
|
```
|
|
</CodeGroup>
|
|
|
|
<Warning>
|
|
Dropping removes the table and **every version of it**.
|
|
[Versioning](/management/table#versioning) does not survive a drop, and neither
|
|
`drop_all_tables` nor `drop_database` asks for confirmation. Both work only on
|
|
a local database; on an Enterprise connection the SDK raises instead.
|
|
</Warning>
|
|
|
|
On a large table the drop can take a while, so `drop_table_async` returns a
|
|
[job](/basics/jobs) rather than blocking when the deployment drops tables in the
|
|
background. In the embedded library, and on an Enterprise cluster that drops inline, the
|
|
returned job has already finished.
|
|
|
|
## Table management APIs {#surface}
|
|
|
|
| | Python | TypeScript | SQL |
|
|
| --- | --- | --- | --- |
|
|
| create | `create_table` | `createTable` | `CREATE TABLE` |
|
|
| create empty | `create_table(schema=)` | `createEmptyTable` | `CREATE TABLE` |
|
|
| open | `open_table` | `openTable` | -- |
|
|
| list | `table_names`, `list_tables` | `tableNames`, `listTables` | -- |
|
|
| exists | `table_exists` | -- | -- |
|
|
| rename | `rename_table` | `renameTable` | -- |
|
|
| clone | `clone_table` | `cloneTable` | -- |
|
|
| drop | `drop_table`, `drop_table_async` | `dropTable`, `dropTableAsync` | `DROP TABLE` |
|
|
| drop all | `drop_all_tables` | `dropAllTables` | -- |
|
|
|
|
## Data types {#data-types}
|
|
|
|
A table's schema is an **Arrow schema**: there is no separate LanceDB type
|
|
system mapped onto storage, so a column is the same object to the query engine
|
|
as it is to the format.
|
|
|
|
| Category | Types |
|
|
| --- | --- |
|
|
| Numeric | `int8`-`int64` and unsigned, `float16`, `float32`, `float64` |
|
|
| String and binary | `utf8`, `large_utf8`, `binary`, `large_binary`, `fixed_size_binary(n)` |
|
|
| Date and time | `date32`, `date64`, `time32`, `time64`, `timestamp`, `duration` |
|
|
| Nested | `struct`, `list`, `large_list`, `fixed_size_list`, `map` |
|
|
| Lance extensions | vectors, [JSON](/sql/data-types#json), [blobs](#multimodal), bfloat16 |
|
|
|
|
A vector column is a `fixed_size_list<float32, dim>`, an ordinary Arrow type,
|
|
which is why it is selectable and joinable but why arithmetic over it is list
|
|
arithmetic rather than scalar.
|
|
|
|
See [SQL data types](/sql/data-types) for the full reference, including the
|
|
extension types and pseudo-columns.
|
|
|
|
## Multimodal data {#multimodal}
|
|
|
|
Store images, audio, video, and documents alongside structured columns and
|
|
embeddings in the same versioned table.
|
|
|
|
Use `blob` columns for these payloads. Small values stay inline in the data
|
|
file, up to 64 KiB by default, and larger values are stored outside it.
|
|
|
|
### Blob columns {#multimodal-blob-v2}
|
|
|
|
<Warning>
|
|
Blob V2 is in early access.
|
|
</Warning>
|
|
|
|
A `blob` column keeps metadata separate from the payload bytes, so queries can
|
|
filter rows without loading the blobs.
|
|
|
|
Most queries return descriptors, which contain metadata about the blob instead
|
|
of its bytes. You can
|
|
[read the bytes in the query results](#multimodal-blob-query-results) or
|
|
[fetch them by row ID](#multimodal-read-blobs).
|
|
|
|
In Python, plain `to_pandas()` scans can also return `BlobFile` handles when
|
|
`pylance` is installed.
|
|
|
|
When you write raw bytes, Lance chooses a storage layout based on the payload
|
|
size. A descriptor's `kind` tells you which one was used:
|
|
|
|
| Storage mode | `kind` | Default placement | Storage behavior |
|
|
| --- | --- | --- | --- |
|
|
| `Inline` | `0` | Up to 64 KiB | Stored in the main data file. |
|
|
| `Packed` | `1` | Above 64 KiB, up to 4 MiB | Packed with other blobs into a shared sidecar file, up to 1 GiB per file by default. |
|
|
| `Dedicated` | `2` | Above 4 MiB | Stored in its own raw sidecar file. |
|
|
| `Managed` | `4` or `5` | Out-of-line values when managed storage is enabled | Stored in a Lance-owned object independently of the data file. |
|
|
|
|
Managed storage must be enabled separately. See
|
|
[Managed blobs](#managed-blobs) for details. `kind=5` identifies a Managed
|
|
object using an explicitly registered base.
|
|
|
|
Lance uses the default thresholds unless you customize them. Explicitly
|
|
configured thresholds are stored in the table schema and preserved on append.
|
|
|
|
#### Setup {#multimodal-blob-v2-setup}
|
|
|
|
Import the SDK and open a local database. The examples in this section use `db`.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsMultimodalImports}{"\n"}{TsBlobApiSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsBlobApiImports}{"\n"}{RsBlobApiSetup}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Define a blob schema {#multimodal-blob-v2-schema}
|
|
|
|
Declare a blob column with `lancedb.blob()` in Python and TypeScript, or
|
|
`blob()` in Rust:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiSchema}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsBlobApiSchema}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsBlobApiSchema}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Ingest blobs {#multimodal-blob-v2-ingest}
|
|
|
|
Pass raw bytes for each blob value. In Python, a blob column accepts `bytes`.
|
|
In TypeScript, it accepts a `Buffer`. The Python example creates two JPEGs
|
|
with EXIF metadata, which we'll read using a blob handle below.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiIngest}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsBlobApiIngest}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsBlobApiIngest}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Read bytes in query results {#multimodal-blob-query-results}
|
|
|
|
Python and TypeScript can return blob bytes in query results without
|
|
collecting `_rowid`. Rust uses [`fetch_blobs`](#multimodal-read-blobs) with
|
|
row IDs instead.
|
|
|
|
##### Python {#multimodal-blob-python}
|
|
|
|
`to_list(blob_mode="bytes")` returns the `image` bytes from the `photos` table
|
|
above:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiBytes}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
In the Python SDK, `Table.to_pandas()`, `Table.to_arrow()`, and the query builder
|
|
methods `to_pandas()`, `to_arrow()`, and `to_list()` accept a `blob_mode`
|
|
argument:
|
|
|
|
| `blob_mode` | `to_pandas()` | `to_arrow()` and `to_list()` |
|
|
| --- | --- | --- |
|
|
| `"lazy"` | Default. Plain scans return seekable `BlobFile` handles. Vector, full-text, hybrid, and ordered queries return descriptor dicts. | Default. Returns descriptors carrying `_lance_row_id` so the result table can be passed to `fetch_blobs` or `fetch_blob_files`. |
|
|
| `"descriptions"` | Returns descriptor dicts with `kind`, `position`, `size`, `blob_id`, and `blob_uri`. | Returns the 5-field descriptor without `_lance_row_id`. |
|
|
| `"bytes"` | Materializes each blob as `bytes` in the DataFrame. | Materializes each blob as `large_binary` in Arrow or `bytes` in Python dicts. |
|
|
|
|
`to_pandas(blob_mode="lazy")` and `to_pandas(blob_mode="bytes")` require
|
|
pylance. Without it they raise `ImportError`. `to_arrow()`,
|
|
`to_list(blob_mode="bytes")`, `to_pandas(blob_mode="descriptions")`, and the
|
|
fetch methods do not.
|
|
|
|
`blob_mode="bytes"` requires a local table snapshot, so remote and in-memory
|
|
tables raise `NotImplementedError`. On a remote table, `Table.to_pandas()` and
|
|
`Table.to_arrow()` aren't supported, and a query's `to_pandas()` also raises in
|
|
the default `"lazy"` mode because it would open `BlobFile` handles. Query with
|
|
`blob_mode="descriptions"`, then read bytes with `fetch_blobs` or
|
|
`fetch_blob_files`.
|
|
|
|
`blob_mode="bytes"` materializes top-level and struct-nested blob columns. It
|
|
raises `ValueError` when the selected columns include blobs
|
|
inside a list. Select those columns with `blob_mode="descriptions"`, or leave
|
|
them out of the projection. Extra keyword arguments on
|
|
`to_pandas()`, such as `split_blocks` and `self_destruct`, are forwarded to
|
|
PyArrow.
|
|
|
|
###### Optional pandas examples {#multimodal-blob-pandas}
|
|
|
|
These examples require pylance. The query example expects `tbl_async`,
|
|
`query_vector`, and a `video` column, which the `photos` table does not define.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiToPandas}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
Sync and async query builders accept the same `blob_mode` values:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{QueryToPandasKwargs}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
##### TypeScript {#multimodal-blob-typescript}
|
|
|
|
In TypeScript, `toArray()` and `toArrow()` accept a `blobMode` option:
|
|
|
|
| `blobMode` | Result |
|
|
| --- | --- |
|
|
| `"descriptions"` | Default. Returns descriptors. |
|
|
| `"bytes"` | Returns each top-level blob as bytes, or `null` for a null blob. Blobs nested in a struct or list keep their descriptors. |
|
|
|
|
With `"bytes"`, the rows and the bytes come from the same table version. If the
|
|
table changes while the query runs, the query runs again, up to three times:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsBlobApiBytesMode}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Read blobs on demand {#multimodal-read-blobs}
|
|
|
|
Query the rows you want and pass their `_rowid` values to a fetch method.
|
|
`fetch_blob_files` opens a seekable handle and loads bytes only when you read.
|
|
The Python example opens the JPEG from the ingest step and reads its EXIF
|
|
Make tag with Pillow. TypeScript and Rust read the first bytes from the handle.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiFiles}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsBlobApiFiles}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsBlobApiFiles}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
The other fetch methods load bytes directly:
|
|
|
|
| Need | Python | TypeScript | Rust |
|
|
| --- | --- | --- | --- |
|
|
| Whole payloads for a set of rows | `fetch_blobs` | `fetchBlobs` | `fetch_blobs` |
|
|
| Byte slices across many rows in one call | `fetch_blob_ranges` | Not available. Use `fetchBlobFiles` and `readRanges`. | `fetch_blob_ranges` |
|
|
|
|
- `fetch_blobs` returns the full bytes for each row ID in order. Null blobs
|
|
produce null entries, and empty blobs produce empty byte strings.
|
|
- `fetch_blob_files` loads bytes when you call `read`, `read_range`, or
|
|
`read_ranges`. Range reads don't move the file cursor. Null blobs produce
|
|
null handles.
|
|
- `fetch_blob_ranges` takes `(row_id, offset, length)` requests and returns one
|
|
slice per request, in order. A request list can repeat a row to read more
|
|
than one slice of that blob.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiFetch}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsBlobApiFetch}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsBlobApiFetch}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
Python and Rust read slices across rows with `fetch_blob_ranges`. On a handle,
|
|
`read_ranges` reads more than one slice of one blob. Python takes
|
|
`(offset, length)` pairs, TypeScript takes `{ start, end }` ranges with an
|
|
exclusive `end`, and Rust takes `start..end` ranges:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{BlobApiRanges}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsBlobApiRanges}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsBlobApiRanges}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Notes and limits {#multimodal-blob-notes}
|
|
|
|
Blob fetches fail if any requested row ID is missing or deleted. With stable
|
|
row IDs enabled, IDs remain valid across compaction.
|
|
|
|
A blob field can sit at the top level of the schema or inside a struct or list.
|
|
Struct-nested blob columns use dotted paths such as `clip.video` in the fetch
|
|
APIs. Blobs inside lists can be written and queried as descriptors. A table
|
|
row ID does not address one list element, so `blob_columns()`, `fetch_blobs`,
|
|
`fetch_blob_files`, and `blob_mode="bytes"` can't return those bytes.
|
|
|
|
See the Lance [blob guide](https://lance.org/guide/blob/) for the storage format.
|
|
|
|
### Managed blobs (Blob v2) {#managed-blobs}
|
|
|
|
Set the table schema metadata `lance.blob.managed` to `true` on a table with
|
|
[`lancedb.blob()`](#multimodal-blob-v2) columns to store small values inline and
|
|
larger values in managed objects under the table's `_blobs/` directory. Medium
|
|
values can share a packed object, and large values use a dedicated object.
|
|
|
|
The table owns these objects. You do not upload them separately, keep an object
|
|
catalog, or delete their files yourself. Compaction preserves each descriptor
|
|
and its payload object, so merging table fragments does not rewrite the blob
|
|
payloads.
|
|
|
|
<Warning>
|
|
Use an SDK and Enterprise deployment that support Blob v2 Managed storage.
|
|
Upgrade every reader, writer, and maintenance worker before activating Managed
|
|
blobs on a table, and reopen existing table handles after the upgrade. Older
|
|
clients cannot safely maintain a table containing Managed blobs.
|
|
</Warning>
|
|
|
|
#### Write and read blobs {#managed-blobs-read-write}
|
|
|
|
This example uses a local connection. The same schema, `add`, query, and
|
|
`fetch_blobs` calls work with an Enterprise database connection:
|
|
|
|
```python
|
|
import lancedb
|
|
import pyarrow as pa
|
|
|
|
db = lancedb.connect("./media")
|
|
table = db.create_table(
|
|
"images",
|
|
schema=pa.schema([
|
|
pa.field("id", pa.int64(), nullable=False),
|
|
lancedb.blob("image"),
|
|
], metadata={"lance.blob.managed": "true"}),
|
|
)
|
|
table.add([
|
|
{"id": 1, "image": b""},
|
|
{"id": 2, "image": bytes(range(256)) * 400},
|
|
{"id": 3, "image": None},
|
|
])
|
|
|
|
# Query descriptors first, then fetch only the selected rows' payloads.
|
|
hits = (
|
|
table.search()
|
|
.where("id <= 2")
|
|
.select(["id", "image"])
|
|
.with_row_id(True)
|
|
.to_arrow()
|
|
)
|
|
images = table.fetch_blobs("image", hits).to_pylist()
|
|
by_id = dict(zip(hits["id"].to_pylist(), images, strict=True))
|
|
assert by_id[1] == b""
|
|
assert len(by_id[2]) == 102400
|
|
```
|
|
|
|
`fetch_blobs` materializes the selected payloads in memory, so fetch bounded
|
|
batches when blobs are large. Row IDs belong to their table and snapshot. Keep
|
|
descriptors opaque in application code, because their representation can differ
|
|
between inline values and Managed objects.
|
|
|
|
#### Derived blobs and maintenance {#managed-blobs-lifecycle}
|
|
|
|
Enterprise Functions can consume blob columns and
|
|
return new blobs. Their output payloads are managed by the destination table.
|
|
Local tables can also derive blob columns with
|
|
[SQL computed columns](/feature-engineering/add-computed-column).
|
|
|
|
Deleting a row or replacing its blob does not immediately remove the old
|
|
payload: retained table versions may still reference it. [Cleanup](/maintenance/cleanup#managed-blobs)
|
|
reclaims objects after those references expire. A packed object remains while
|
|
any of its contained blobs is still referenced.
|
|
|
|
### Legacy large-binary blobs {#multimodal-large-blobs-blob-api}
|
|
|
|
Use [`lancedb.blob()`](#multimodal-blob-v2) for new tables. The sections below
|
|
cover existing tables that use the V1 large-binary Blob API
|
|
(`pa.large_binary()` in Python, `LargeBinary` in TypeScript, and
|
|
`DataType::LargeBinary` in Rust with `{"lance-encoding:blob": "true"}` field
|
|
metadata). Changing a field's metadata alone does not migrate an existing V1
|
|
column to Blob v2.
|
|
|
|
#### 1\. Define a blob schema {#multimodal-1-define-a-blob-schema}
|
|
|
|
V1 blob columns use a `large_binary` field marked with `{"lance-encoding:blob": "true"}` metadata:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{LegacyBlobApiSchema}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsLegacyBlobApiSchema}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsLegacyBlobApiSchema}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### 2\. Ingest large blobs {#multimodal-2-ingest-large-blobs}
|
|
|
|
Write raw bytes to a V1 blob column:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{LegacyBlobApiIngest}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsLegacyBlobApiIngest}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsLegacyBlobApiIngest}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### 3\. Convert blob tables to pandas {#multimodal-3-convert-blob-tables-to-pandas}
|
|
|
|
On local tables with V1 blob columns, `to_pandas()` in Python accepts `blob_mode`
|
|
(`"lazy"`, `"bytes"`, or `"descriptions"`). Remote tables raise
|
|
`NotImplementedError`, and `"bytes"` is not supported on in-memory tables.
|
|
Extra keyword arguments such as `split_blocks` or `self_destruct` are forwarded
|
|
to the PyArrow / Lance pandas conversion:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{LegacyBlobApiToPandas}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
On query builders with V1 blob columns, plain scans support `"lazy"`, `"bytes"`,
|
|
and `"descriptions"`. Vector, FTS, hybrid, and ordered queries cannot
|
|
materialize V1 blob columns through `to_pandas()`, so omit those columns from
|
|
the projection:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{LegacyQueryToPandasKwargs}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
### Store binary data {#multimodal-store-binary-data}
|
|
|
|
Use a `binary` column when the query should return the raw bytes. Declare it as `pa.binary()` in Python, `Binary` in TypeScript, and `DataType::Binary` in Rust.
|
|
|
|
#### 1\. Setup and imports {#multimodal-1-setup-and-imports}
|
|
|
|
Import LanceDB and Arrow in your SDK.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{MultimodalImports}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsMultimodalImports}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsMultimodalImports}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### 2\. Prepare data {#multimodal-2-prepare-data}
|
|
|
|
Encode each image or file into raw bytes before writing.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{CreateDummyData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsCreateDummyData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsCreateDummyData}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### 3\. Define the schema {#multimodal-3-define-the-schema}
|
|
|
|
Declare the schema explicitly so Arrow and LanceDB store the field as `binary` instead of inferring a string or integer list.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{DefineSchema}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsDefineSchema}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsDefineSchema}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### 4\. Ingest data {#multimodal-4-ingest-data}
|
|
|
|
Create the table from the rows and schema.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{IngestData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsIngestData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsIngestData}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
### Retrieve binary data {#multimodal-retrieve-and-use-blobs}
|
|
|
|
When you search the table, select the `binary` column alongside your metadata columns.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{SearchData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsSearchData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsSearchData}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Convert bytes back to objects {#multimodal-convert-bytes-back-to-objects}
|
|
|
|
Decode the returned bytes into an image or media buffer in your application.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{ProcessResults}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsProcessResults}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsProcessResults}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
### Other modalities {#multimodal-other-modalities}
|
|
|
|
For PDFs, audio recordings, and other files, keep searchable fields in separate
|
|
columns so you can filter rows before fetching blob bytes.
|
|
|
|
### bfloat16 {#multimodal-bfloat16}
|
|
|
|
Sixteen-bit floats in the bfloat16 layout, stored as `fixed_size_binary(2)`.
|
|
Useful as a vector element type when halving the memory matters more than the
|
|
precision does.
|
|
|
|
## Schema evolution {#schema}
|
|
|
|
Schema evolution enables non-breaking modifications to a database table's structure (such as adding columns, altering data types, or dropping fields) to adapt to evolving data requirements without service interruptions.
|
|
LanceDB supports ACID-compliant schema evolution through granular operations (add/alter/drop columns), allowing you to:
|
|
|
|
* Iterate Safely: Modify schemas in production with versioned datasets and backward compatibility
|
|
* Scale Seamlessly: Handle ML model iterations, regulatory changes, or feature additions
|
|
* Optimize Continuously: Remove unused fields or enforce new constraints without downtime
|
|
|
|
<Note>
|
|
In SQL, schema changes are [`ALTER TABLE`](/sql/ddl/table#alter).
|
|
</Note>
|
|
|
|
### Schema evolution operations {#schema-schema-evolution-operations}
|
|
|
|
LanceDB supports four primary schema evolution operations:
|
|
|
|
1. **Adding new columns**: Extend your table with additional attributes
|
|
2. **Altering existing columns**: Change column names, data types, or nullability
|
|
3. **Updating field metadata**: Attach or change per-column Arrow metadata
|
|
4. **Dropping columns**: Remove unnecessary columns from your schema
|
|
|
|
|
|
<Tip title="Schema Evolution Performance">
|
|
Schema evolution operations are applied immediately but do not typically require rewriting all data. However, data type changes may involve more substantial operations.
|
|
</Tip>
|
|
|
|
Each schema evolution operation commits a new table version and returns status metadata such as
|
|
the committed `version`. Run these operations from a mutable table handle; if you checked out an
|
|
older version for reads, call `checkout_latest` / `checkoutLatest` before modifying the schema.
|
|
|
|
### Add new columns {#schema-add-new-columns}
|
|
|
|
You can add new columns to a table with the [`add_columns`](https://lancedb.github.io/lancedb/python/python/#lancedb.table.Table.add_columns)
|
|
method in Python, [`addColumns`](https://lancedb.github.io/lancedb/js/classes/Table/#addcolumns) in TypeScript/JavaScript, or `add_columns` in Rust.
|
|
New columns are populated based on SQL expressions you provide.
|
|
|
|
|
|
#### Set up the example table {#schema-set-up-the-example-table}
|
|
|
|
First, let's create a sample table with product data to demonstrate schema evolution:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{PySchemaAddSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsSchemaAddSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsSchemaAddSetup}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
<Note>
|
|
Columns derived from existing data (SQL expressions over other columns) are
|
|
covered in [Data evolution](/feature-engineering/data-evolution), because
|
|
deriving values is a feature-engineering job rather than a schema one.
|
|
</Note>
|
|
|
|
#### Add columns with default values {#schema-add-columns-with-default-values}
|
|
|
|
Add boolean columns with default values for status tracking:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AddColumnsDefaultValues}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAddColumnsDefaultValues}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAddColumnsDefaultValues}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Add nullable columns {#schema-add-nullable-columns}
|
|
|
|
Add timestamp columns that can contain NULL values:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AddColumnsNullable}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAddColumnsNullable}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAddColumnsNullable}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
<Warning title="NULL Values in New Columns">
|
|
When adding columns that should contain NULL values, be sure to cast the NULL to the appropriate type, e.g., `cast(NULL as timestamp)`.
|
|
</Warning>
|
|
|
|
### Alter existing columns {#schema-alter-existing-columns}
|
|
|
|
You can alter columns using the [`alter_columns`](https://lancedb.github.io/lancedb/python/python/#lancedb.table.Table.alter_columns)
|
|
method in Python, [`alterColumns`](https://lancedb.github.io/lancedb/js/classes/Table/#altercolumns) in TypeScript/JavaScript, or `alter_columns` in Rust. This allows you to:
|
|
|
|
- Rename a column
|
|
- Change a column's data type
|
|
- Modify nullability (whether a column can contain NULL values)
|
|
|
|
|
|
#### Set up the example table {#schema-set-up-the-example-table-2}
|
|
|
|
Create a table with a custom schema to demonstrate column alterations:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{SchemaAlterSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsSchemaAlterSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsSchemaAlterSetup}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Rename columns {#schema-rename-columns}
|
|
|
|
Change column names to better reflect their purpose:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AlterColumnsRename}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAlterColumnsRename}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAlterColumnsRename}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Change data types {#schema-change-data-types}
|
|
|
|
Convert column data types for better performance or compatibility:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AlterColumnsDataType}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAlterColumnsDataType}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAlterColumnsDataType}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Make columns nullable {#schema-make-columns-nullable}
|
|
|
|
You can alter columns to contain NULL values:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AlterColumnsNullable}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAlterColumnsNullable}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAlterColumnsNullable}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
Changing a column to nullable affects future writes and merges too: missing values are accepted
|
|
only when the target column is nullable.
|
|
|
|
#### Multiple changes at once {#schema-multiple-changes-at-once}
|
|
|
|
Apply several alterations in a single operation:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AlterColumnsMultiple}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAlterColumnsMultiple}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAlterColumnsMultiple}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Expression-based type changes {#schema-expression-based-type-changes}
|
|
|
|
For transformations that are not simple casts (for example, converting `"$100"` to an integer), use a SQL-expression column add, then drop and rename:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AlterColumnsWithExpression}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAlterColumnsWithExpression}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAlterColumnsWithExpression}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Alter embedding types and dimensions {#schema-alter-embedding-types-and-dimensions}
|
|
|
|
It's quite common to need to change an embedding column's schema, in case a new model becomes available with a different embedding dimension.
|
|
- In Python, the example shows an in-place type update when the cast is compatible.
|
|
- In TypeScript and Rust, the example shows a dimension change (`384 -> 1024`), which cannot be cast in-place.
|
|
|
|
For dimension changes, use this 3-step pattern: add a new column with the target type, drop the old column, then rename the new column to the original name.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{AlterVectorColumn}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsAlterVectorColumn}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsAlterVectorColumn}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
<Warning>
|
|
**`FixedSizeList` Dimension Changes in TypeScript and Rust**
|
|
|
|
`alterColumns` / `alter_columns` can cast between compatible types, but changing `FixedSizeList` dimensions (for example `384 -> 1024`) is not a compatible cast.
|
|
For such cases, use `addColumns` / `add_columns` (with `arrow_cast`), then `dropColumns` / `drop_columns`, then rename the replacement column.
|
|
</Warning>
|
|
|
|
<Warning title="Data Type Changes">
|
|
Changing data types requires rewriting the column data and may be resource-intensive for large tables. Renaming columns or changing nullability is more efficient as it only updates metadata.
|
|
</Warning>
|
|
|
|
### Update field metadata {#schema-update-field-metadata}
|
|
|
|
Each column in a LanceDB table can carry a small key/value map of Arrow field metadata, which is useful
|
|
for annotating columns with units, provenance, PII flags, embedding model versions, or any other
|
|
schema-level context your application needs.
|
|
|
|
Use [`update_field_metadata`](https://lancedb.github.io/lancedb/python/python/#lancedb.table.Table.update_field_metadata)
|
|
in Python, [`updateFieldMetadata`](https://lancedb.github.io/lancedb/js/classes/Table/#updatefieldmetadata)
|
|
in TypeScript/JavaScript, or `update_field_metadata` in Rust to add, change, or remove these
|
|
key/value pairs without rewriting the column data. Each call commits a new table version and returns
|
|
the new `version`.
|
|
|
|
Each update targets one field by **dot-path**: top-level columns are addressed by name (for
|
|
example `"embedding"`), and nested fields by their full path (for example `"address.zip"`). By
|
|
default, the keys you pass are **merged** into the field's existing metadata: keys you do not
|
|
mention are preserved, and passing `None` (Python) or `null` (TypeScript) deletes a key. Set
|
|
`replace: true` to swap the field's entire metadata map instead of merging.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{SchemaFieldMetadataMerge}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsSchemaFieldMetadataMerge}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsSchemaFieldMetadataMerge}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
To overwrite a field's metadata entirely instead of merging, set `replace` to `true`:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{SchemaFieldMetadataReplace}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsSchemaFieldMetadataReplace}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsSchemaFieldMetadataReplace}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
<Tip>
|
|
You can pass multiple updates in a single call to change metadata on several fields at once.
|
|
Each call commits a single new table version.
|
|
</Tip>
|
|
|
|
#### Canonical metadata keys {#schema-canonical-metadata-keys}
|
|
|
|
You can use any string as a metadata key. By convention, LanceDB treats keys prefixed with
|
|
`lancedb:` as canonical. LanceDB Enterprise displays these keys in the UI, and agents can rely
|
|
on finding this information in a consistent place. Use them when they fit your use case:
|
|
|
|
| Key | Purpose | Example |
|
|
| --- | --- | --- |
|
|
| `lancedb:description` | Human-readable description of the field. | `"CLIP embedding of the product image"` |
|
|
| `lancedb:tag:<name>` | User-defined key/value tag, where the suffix names the tag category. | `"lancedb:tag:model": "clip"` |
|
|
| `lancedb:logical-column` | Groups related columns into one logical column. For example, `feature_v1` and `feature_v2` might belong to the same logical column. | `"lancedb:logical-column": "feature"` |
|
|
| `lancedb:status` | Life cycle state of the column: `production`, `candidate`, `deprecated`, or `archived`. | `"lancedb:status": "production"` |
|
|
|
|
<Note>
|
|
These keys are conventions, not enforced constraints. LanceDB does not validate their values,
|
|
but following the conventions keeps your tables consistent with Enterprise tooling.
|
|
</Note>
|
|
|
|
### Drop columns {#schema-drop-columns}
|
|
|
|
You can remove columns using the [`drop_columns`](https://lancedb.github.io/lancedb/python/python/#lancedb.table.Table.drop_columns)
|
|
method in Python, [`dropColumns`](https://lancedb.github.io/lancedb/js/classes/Table/#dropcolumns) in TypeScript/JavaScript, or `drop_columns` in Rust.
|
|
|
|
|
|
#### Set Up the example table {#schema-set-up-the-example-table-3}
|
|
|
|
Create a table with temporary columns that we'll remove:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{SchemaDropSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsSchemaDropSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsSchemaDropSetup}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Drop single columns {#schema-drop-single-columns}
|
|
|
|
Remove individual columns that are no longer needed:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{DropColumnsSingle}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsDropColumnsSingle}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsDropColumnsSingle}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Drop multiple columns {#schema-drop-multiple-columns}
|
|
|
|
Remove several columns at once for efficiency:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{DropColumnsMultiple}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsDropColumnsMultiple}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsDropColumnsMultiple}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
<Warning title="Dropping a column is undone only by restoring a version">
|
|
Dropping a column changes the latest version only. Until
|
|
[cleanup](/maintenance/cleanup) prunes the versions from before the drop,
|
|
[restoring](#versioning) one of them brings the column and its values back,
|
|
along with the rest of the table as it was then. Once those versions are
|
|
pruned, the drop cannot be undone, so check before dropping a column you may
|
|
need.
|
|
</Warning>
|
|
|
|
## Versioning {#versioning}
|
|
|
|
This page shows the core table-versioning APIs used in the code snippets for Python, TypeScript, and Rust.
|
|
Each operation below maps directly to methods shown in the examples.
|
|
|
|
### Basic Versioning Example {#versioning-basic-versioning-example}
|
|
|
|
Let's create a table with sample data to demonstrate LanceDB's versioning capabilities:
|
|
|
|
#### Set Up the Table {#versioning-set-up-the-table}
|
|
|
|
First, let's create a table with some sample data:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningBasicSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningBasicSetup}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningBasicSetup}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
<Expandable title="Rust helper used in this page">
|
|
<CodeGroup>
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningMakeQuotesReader}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
</Expandable>
|
|
|
|
#### Check Initial Version {#versioning-check-initial-version}
|
|
|
|
After creating the table, let's check the initial version information:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningCheckInitialVersion}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningCheckInitialVersion}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningCheckInitialVersion}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
### Modify Data {#versioning-modify-data}
|
|
|
|
When you modify data through operations like update or delete, LanceDB automatically creates new versions.
|
|
|
|
#### Update Existing Data {#versioning-update-existing-data}
|
|
|
|
Let's update some existing records to see versioning in action:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningUpdateData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningUpdateData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningUpdateData}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Add New Data {#versioning-add-new-data}
|
|
|
|
Now let's add more records to the table:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningAddData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningAddData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningAddData}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Check Version Changes {#versioning-check-version-changes}
|
|
|
|
Let's see how the versions have changed after our modifications:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningCheckVersionsAfterMod}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningCheckVersionsAfterMod}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningCheckVersionsAfterMod}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
### Rollback to Previous Versions {#versioning-rollback-to-previous-versions}
|
|
|
|
LanceDB supports fast rollbacks to any previous version without data duplication.
|
|
|
|
#### View All Versions {#versioning-view-all-versions}
|
|
|
|
First, let's see all the versions we've created:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningListAllVersions}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningListAllVersions}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningListAllVersions}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Restore a Version Snapshot {#versioning-restore-a-version-snapshot}
|
|
|
|
Now let's restore a captured version snapshot:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningRollback}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningRollback}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningRollback}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
### Tag-Based Versioning {#versioning-tag-based-versioning}
|
|
|
|
Numeric table versions like `v3` or `v17` are precise but hard to remember. Tags
|
|
let you attach human-readable labels (e.g., `"prod"`, `"baseline"`,
|
|
`"q3-evaluation"`) to specific versions and check those out by name. They are
|
|
conceptually similar to git tags, and unlike numeric versions, **tagged versions
|
|
are never pruned**. A tagged version that is older than the retention window and
|
|
is not the latest stops cleanup instead (see the cleanup note at the bottom of this
|
|
page).
|
|
|
|
The tags API supports the standard CRUD operations (create, list, update, delete),
|
|
plus checking out by tag name.
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningTags}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningTags}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningTags}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
<Note>
|
|
Deleting a tag only removes the label, not the version it points to. After
|
|
deletion, the underlying table version becomes eligible for cleanup again.
|
|
</Note>
|
|
|
|
### Branches {#versioning-branches}
|
|
|
|
Beyond linear history, LanceDB also supports **branches**: isolated, writable
|
|
lines of history forked from `main` (or a specific version). Whereas tags and
|
|
`checkout` give you read-only views of existing versions, a branch has its own
|
|
writable history, making it ideal for experiments, backfills, and migrations
|
|
that you want to keep separate from production reads on `main`.
|
|
|
|
Branches are covered in their own guide: see [Branches](/tables/branching).
|
|
|
|
### Delete Data From the Table {#versioning-delete-data-from-the-table}
|
|
|
|
Let's demonstrate how deletions also create new versions:
|
|
|
|
#### Go Back to Latest Version {#versioning-go-back-to-latest-version}
|
|
|
|
First, let's return to the latest version:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningCheckoutLatest}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningCheckoutLatest}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningCheckoutLatest}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Delete Data {#versioning-delete-data}
|
|
|
|
Now let's delete some data to see how it affects versioning:
|
|
|
|
<CodeGroup>
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{VersioningDeleteData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="TypeScript" language="TypeScript" icon="square-js">
|
|
{TsVersioningDeleteData}
|
|
</CodeBlock>
|
|
|
|
<CodeBlock filename="Rust" language="Rust" icon="rust">
|
|
{RsVersioningDeleteData}
|
|
</CodeBlock>
|
|
</CodeGroup>
|
|
|
|
#### Version History and Operations {#versioning-version-history-and-operations}
|
|
|
|
On a fresh table, the snippets in this guide produce this version sequence:
|
|
|
|
1. `v1`: create table (`create_table` / `createTable` / `create_table`)
|
|
2. `v2`: update rows (`update`)
|
|
3. `v3`: add rows (`add`)
|
|
4. `v4`: restore snapshot (`restore`) from `version_after_mod`/`versionAfterMod`
|
|
5. `v5`: delete rows (`delete`)
|
|
|
|
Read-only and checkout operations shown here (`list_versions`/`listVersions`, `version`, `checkout`, `checkout_latest`/`checkoutLatest`) do not create new versions.
|
|
|
|
The version metadata fields can differ by backend. Direct table-backed version listing exposes a
|
|
timestamp, while namespace-backed listing may expose fields such as `manifest_path`,
|
|
`manifest_size`, `e_tag`, and `timestamp_millis`. In deployments that use managed versioning,
|
|
prefer the table version APIs exposed by LanceDB Enterprise or the namespace service instead of
|
|
mixing in lower-level Lance file operations.
|
|
|
|
<Note>
|
|
**System Operations**
|
|
|
|
System operations like `optimize()`, index updates, and table compaction also increment table version numbers.
|
|
In LanceDB OSS, `optimize()` can prune older versions based on its retention setting (`cleanup_older_than`, 7 days by default),
|
|
which is when old-version files are removed and disk space is reclaimed. A deployment prunes them in the background
|
|
when its configuration enables [cleanup](/maintenance/cleanup); `optimize()` is not available there.
|
|
|
|
**Tagged versions are never pruned.** A version with a tag pointing at it is
|
|
retained regardless of age. When a tagged version is older than the retention window and
|
|
is not the latest, cleanup refuses to run instead of pruning around it: `optimize()` raises
|
|
an error naming the tag and prunes nothing. A tag on the latest version does not cause
|
|
this, but `optimize()` compacts before it prunes, and compacting commits a new version
|
|
whenever it merges files, so a tag on what was the latest version can become an older
|
|
one by the time cleanup runs; the error then comes after compacting, and the index
|
|
update does not run. To let cleanup proceed, [move the tag](#versioning-tag-based-versioning)
|
|
to a version inside the retention window, delete it, or widen the window.
|
|
</Note>
|