Skip to content
Merged
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
22 changes: 13 additions & 9 deletions docs/docs/pypaimon/multimodal-tables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ primary keys. Each `add`, `overwrite`, `update`, or `delete` call handles its ow
commit; use the [Python API](./python-api) for explicit writer and commit control.

Creating a multimodal table enables `row-tracking.enabled`,
`data-evolution.enabled`, `deletion-vectors.enabled`, and
`blob-as-descriptor` by default. Global index scans use full fallback coverage
`data-evolution.enabled`, and `blob-as-descriptor` by default.
Global index scans use full fallback coverage
through `global-index.search-mode` set to `FULL`. Data files use Paimon's
default Parquet format, and vector columns are stored in dedicated Parquet
files through `vector.file.format` set to `parquet`. These defaults require no
file-format extra:
default Parquet format, including vector columns. To store vectors in dedicated
files, explicitly set `vector.file.format`, for example to `parquet`.
Deletion vectors are disabled by default; enable them with
`deletion-vectors.enabled` set to `true` in the table creation options.
These defaults require no file-format extra:

```shell
pip install pypaimon
Expand Down Expand Up @@ -365,9 +367,10 @@ or delete prepared files after a commit error.
## Delete

`delete` removes rows matched by a SQL-like predicate. It uses the same
predicate syntax as `scan().where(...)` and `update(...)`. Multimodal tables
enable deletion vectors by default; on partitioned tables, a predicate that
references only partition columns uses the partition drop path.
predicate syntax as `scan().where(...)` and `update(...)`. Row-level deletes
require `deletion-vectors.enabled` set to `true` when creating the table.
On partitioned tables, a predicate that references only partition columns uses
the partition drop path.

```python
docs.delete(where="id = 2")
Expand Down Expand Up @@ -405,7 +408,8 @@ docs.merge("id") \
])
```

Use `when_matched_delete()` for matched rows that should be removed:
Use `when_matched_delete()` for matched rows that should be removed. The table
must have `deletion-vectors.enabled` set to `true`:

```python
docs.merge("id") \
Expand Down
2 changes: 0 additions & 2 deletions paimon-python/pypaimon/multimodal/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@
_DEFAULT_OPTIONS = {
"row-tracking.enabled": "true",
"data-evolution.enabled": "true",
"deletion-vectors.enabled": "true",
"blob-as-descriptor": "true",
"global-index.search-mode": "full",
"vector.file.format": "parquet",
}

_DEFAULT_DATABASE = Catalog.DEFAULT_DATABASE
Expand Down
1 change: 1 addition & 0 deletions paimon-python/pypaimon/tests/batch_vector_lookup_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def docs(tmp_path, request):
table = pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"docs", schema=schema, partitioned=["category"], options={
"file.format": "parquet", "vector.file.format": "parquet",
"deletion-vectors.enabled": "true",
"vector-index.search-mode": "full", "read.parallelism": str(request.param),
"read.batch-size": "2", "blob-as-descriptor": "false",
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def docs(tmp_path):
table = pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"docs", schema=schema, partitioned=["pt"], options={
"file.format": "parquet", "vector.file.format": "parquet", "read.batch-size": "1",
"deletion-vectors.enabled": "true",
"full-text-index.search-mode": "full", "vector-index.search-mode": "full"})
table.add(pa.table({"id": [0, 1, 2], "text": ["paimon", "paimon paimon", "paimon long document"],
"label": ["other", "other", "target"], "pt": ["a"] * 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@

def test_scoped_live_rows_only_load_relevant_deletions(tmp_path):
table = pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"rows", schema=pa.schema([("id", pa.int64())]), options={"file.format": "parquet"})
"rows", schema=pa.schema([("id", pa.int64())]), options={
"file.format": "parquet", "deletion-vectors.enabled": "true"})
table.add(pa.table({"id": [0, 1, 2]}))
table.add(pa.table({"id": [3, 4, 5]}))
snapshot = table.raw_table.snapshot_manager().get_latest_snapshot()
Expand Down
3 changes: 2 additions & 1 deletion paimon-python/pypaimon/tests/multimodal_columns_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def frames(tmp_path):
]),
data=[{"id": i, "label": "old", "image": bytes([i]) * 100}
for i in range(5)],
options={"file.format": "parquet", "read.batch-size": "2"},
options={"file.format": "parquet", "read.batch-size": "2",
"deletion-vectors.enabled": "true"},
)


Expand Down
2 changes: 1 addition & 1 deletion paimon-python/pypaimon/tests/multimodal_lance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def test_export_lance_exports_logical_updates_deletes_and_historical_schema(conn
}, [
{"id": 1, "text": "before", "image": b"before"},
{"id": 2, "text": "deleted", "image": b"deleted"},
])
], options={"deletion-vectors.enabled": "true"})
snapshot_id = table.raw_table.snapshot_manager().get_latest_snapshot().id
table.raw_table.create_tag("training-v1", snapshot_id=snapshot_id)
conn.catalog.alter_table("default.history", [
Expand Down
4 changes: 2 additions & 2 deletions paimon-python/pypaimon/tests/multimodal_table_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ def test_create_table_defaults_data_evolution_options(self):
options = table.raw_table.table_schema.options
self.assertEqual("true", options["row-tracking.enabled"])
self.assertEqual("true", options["data-evolution.enabled"])
self.assertEqual("true", options["deletion-vectors.enabled"])
self.assertNotIn("deletion-vectors.enabled", options)
self.assertEqual("true", options["blob-as-descriptor"])
self.assertNotIn("data-evolution.row-sidecar.enabled", options)
self.assertEqual("full", options["global-index.search-mode"])
self.assertEqual("parquet", options["vector.file.format"])
self.assertNotIn("vector.file.format", options)
self.assertEqual("default.docs", self.conn.get_table("docs").identifier)
self.assertEqual(["id", "content", "embedding", "payload"],
[field.name for field in table.raw_table.fields])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def test_worker_filters_overlapping_scalar_shards_at_planned_snapshot(tmp_path,
table = pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"vectors", schema=schema, options={
"file.format": "parquet", "vector.file.format": "parquet",
"deletion-vectors.enabled": "true",
"global-index.filter.refine-from-data": "true"})
for start in range(0, 12, 3):
row_ids = list(range(start, start + 3))
Expand Down
1 change: 1 addition & 0 deletions paimon-python/pypaimon/tests/ray_vector_search_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def table(tmp_path):
return pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"vectors", schema=SCHEMA,
options={"file.format": "parquet", "vector.file.format": "parquet",
"deletion-vectors.enabled": "true",
"vector-index.search-mode": "full", "read.batch-size": "2",
"source.split.target-size": "1 b", "source.split.open-file-cost": "1 b"})

Expand Down
3 changes: 2 additions & 1 deletion paimon-python/pypaimon/tests/search_result_metadata_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def docs(tmp_path):
("embedding", pa.list_(pa.float32(), 2))])
table = pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"docs", schema=schema, options={"file.format": "parquet", "vector.file.format": "parquet",
"vector-index.search-mode": "full", "read.batch-size": "1"})
"vector-index.search-mode": "full", "read.batch-size": "1",
"deletion-vectors.enabled": "true"})
table.add(pa.table({"id": [0, 1, 2, 3], "text": ["paimon long document", "paimon", "other", "paimon"],
"embedding": [[2., 1.], [0., 1.], [1., 1.], [0., 1.]]}, schema=schema))
return table
Expand Down
2 changes: 1 addition & 1 deletion paimon-python/pypaimon/tests/search_snapshot_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def table(tmp_path):
return pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"vectors", schema=SCHEMA,
options={"file.format": "parquet", "vector.file.format": "parquet",
"vector-index.search-mode": "full"})
"vector-index.search-mode": "full", "deletion-vectors.enabled": "true"})


def search(table, kind="vector", **options):
Expand Down
4 changes: 3 additions & 1 deletion paimon-python/pypaimon/tests/torch_read_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2045,7 +2045,9 @@ def test_torch_batch_shuffle_vectors_deletions_and_snapshot(self):

schema = pa.schema([('id', pa.int64()), ('vector', pa.list_(pa.float32(), 2))])
table = pm.connect(options={'warehouse': self.warehouse}).create_table(
'batch_shuffle_vectors', schema=schema, options={'file.format': 'parquet'})
'batch_shuffle_vectors', schema=schema, options={
'file.format': 'parquet', 'vector.file.format': 'parquet',
'deletion-vectors.enabled': 'true'})
table.add(pa.table({'id': list(range(8)), 'vector': [[i, i + 1] for i in range(8)]}, schema=schema))
snapshot = table.raw_table.snapshot_manager().get_latest_snapshot().id
table.delete('id = 2')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def table(tmp_path):
schema = pa.schema([("id", pa.int64()), ("name", pa.string()), ("embedding", pa.list_(pa.float32(), 2))])
table = pm.connect(options={"warehouse": str(tmp_path)}).create_table(
"vectors", schema=schema, options={"file.format": "parquet", "vector.file.format": "parquet",
"read.batch-size": "1"})
"read.batch-size": "1", "deletion-vectors.enabled": "true"})
table.add(pa.table({"id": [0, 1, 2], "name": ["alpha", "beta zeta", "gamma"],
"embedding": [[0., 1.], [1., 1.], [2., 1.]]}, schema=schema))
table.raw_table.create_global_index(
Expand Down
8 changes: 6 additions & 2 deletions paimon-python/pypaimon/tests/vector_index_dv_build_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def create_table(tmp_path, null_vector=False):
table = pm.connect(options={'warehouse': str(tmp_path)}).create_table(
'vectors', schema=schema, options={
'file.format': 'parquet', 'vector.file.format': 'parquet', 'read.batch-size': '2',
'deletion-vectors.enabled': 'true',
'global-index.row-count-per-shard': '4', 'global-index.build.parallelism': '1'})
append(table, list(range(7)), null_vector)
return table
Expand Down Expand Up @@ -123,7 +124,8 @@ def test_python_ivfpq_search_keeps_live_null_vectors_out_of_results(tmp_path, ba
schema = pa.schema([('id', pa.int64()), ('embedding', pa.list_(pa.float32()))])
table = pm.connect(options={'warehouse': str(tmp_path)}).create_table(
'vectors', schema=schema, options={
'file.format': 'parquet', 'vector.file.format': 'parquet', 'vector-index.search-mode': 'fast'})
'file.format': 'parquet', 'vector.file.format': 'parquet', 'vector-index.search-mode': 'fast',
'deletion-vectors.enabled': 'true'})
table.add(pa.table({
'id': list(range(5)), 'embedding': [[1., 0.], [0., 1.], None, [1., 1.], [2., 2.]]}, schema=schema))
table.delete('id = 0 OR id = 3')
Expand Down Expand Up @@ -178,7 +180,9 @@ def test_native_incremental_build_with_concurrent_delete(table):
def test_native_partition_scoped_build_after_deletion(tmp_path):
pytest.importorskip('paimon_vindex')
table = pm.connect(options={'warehouse': str(tmp_path)}).create_table(
'vectors', schema=SCHEMA, partitioned=['category'], options={'file.format': 'parquet'})
'vectors', schema=SCHEMA, partitioned=['category'], options={
'file.format': 'parquet', 'vector.file.format': 'parquet',
'deletion-vectors.enabled': 'true'})
append(table, [0, 1, 2], category='other')
append(table, [3, 4, 5])
table.delete("category = 'yes' AND id = 4")
Expand Down
Loading