Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,64 @@ from daft_lance import merge_columns_df
merge_columns_df(df, "s3://bucket/my_dataset")
```

### Namespace Tables

Address Lance tables through a [Lance Namespace](https://lancedb.github.io/lance-namespace/)
(catalog) instead of a raw URI. Pass `namespace_impl` + `namespace_properties` + `table_id`
in place of `uri` — the namespace resolves the table's storage location and vends any storage
credentials. This works across `read_lance`, `write_lance`, `merge_columns_df`,
`create_scalar_index`, and `compact_files`.

```python
import daft
import daft_lance

table_id = ["my_table"]
namespace = {"namespace_impl": "dir", "namespace_properties": {"root": "/tmp/lance_tables"}}

daft_lance.write_lance(
daft.from_pydict({"id": [1, 2, 3]}),
table_id=table_id,
mode="create",
**namespace,
).collect()

df = daft_lance.read_lance(table_id=table_id, **namespace)
```

`uri` and the namespace parameters are mutually exclusive: provide exactly one of `uri` or
(`namespace_impl` + `table_id`).

#### Using a REST namespace (e.g. Gravitino Lance REST server)

```python
namespace = {
"namespace_impl": "rest",
"namespace_properties": {"uri": "http://127.0.0.1:9101/lance"},
}
table_id = ["lance_catalog", "sales", "orders"]

daft_lance.write_lance(df, table_id=table_id, mode="create", **namespace).collect()
daft_lance.read_lance(table_id=table_id, **namespace).show()
```

When the catalog holds the storage configuration (bucket, endpoint, credentials), the
`describe_table` response vends `storage_options` to the client, so you do not need to pass
object-store credentials yourself.

#### Drop-in Daft APIs

If you prefer Daft's own entry points, call `daft_lance.patch_daft()` once to route
`daft.read_lance` and `DataFrame.write_lance` through daft-lance (namespace parameters included):

```python
import daft, daft_lance

daft_lance.patch_daft()
df.write_lance(table_id=["my_table"], mode="create", **namespace).collect()
daft.read_lance(table_id=["my_table"], **namespace).show()
```

## Migration

The migration only requires replacing `daft.io.lance` with `daft_lance`.
Expand Down
48 changes: 48 additions & 0 deletions daft_lance/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from __future__ import annotations

from typing import Any, Literal

try:
import daft
except ImportError:
Expand All @@ -10,13 +14,57 @@
merge_columns,
merge_columns_df,
read_lance,
write_lance,
)
from .lance_data_sink import LanceDataSink


def patch_daft() -> None:
"""Route ``daft.read_lance`` and ``DataFrame.write_lance`` through daft-lance.

Optional convenience for drop-in use of namespace parameters with Daft's own
APIs, e.g. ``df.write_lance(namespace_impl="dir", ...)``. Idempotent.
"""
daft.read_lance = read_lance # type: ignore[assignment]

from daft.dataframe import DataFrame

if getattr(DataFrame.write_lance, "_daft_lance_patch", False):
return

def _write_lance(
self: DataFrame,
uri: Any = None,
mode: Literal["create", "append", "overwrite", "merge"] = "create",
io_config: Any | None = None,
schema: Any | None = None,
left_on: str | None = None,
right_on: str | None = None,
**kwargs: Any,
) -> DataFrame:
return write_lance(
self,
uri,
mode=mode,
io_config=io_config,
schema=schema,
left_on=left_on,
right_on=right_on,
**kwargs,
)

_write_lance._daft_lance_patch = True # type: ignore[attr-defined]
DataFrame.write_lance = _write_lance # type: ignore[method-assign]


__all__ = [
"LanceDataSink",
"compact_files",
"create_scalar_index",
"merge_columns",
"merge_columns_df",
"patch_daft",
"read_lance",
"take_blobs",
"write_lance",
]
Loading
Loading