Skip to content

Commit 7ebeeae

Browse files
authored
Merge pull request #159 from weaviate/jose/collection-export
Add collection export commands (create, get, cancel).
2 parents 1a469a0 + 065c6fc commit 7ebeeae

13 files changed

Lines changed: 1141 additions & 7 deletions

File tree

.claude/skills/contributing-to-weaviate-cli/references/architecture.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class CollectionManager:
8181
self.client.collections.create(name=collection, ...)
8282
```
8383

84+
Manager files: `collection_manager.py`, `tenant_manager.py`, `data_manager.py`, `backup_manager.py`, `export_manager.py`, `role_manager.py`, `user_manager.py`, `node_manager.py`, `shard_manager.py`, `cluster_manager.py`, `alias_manager.py`, `benchmark_manager.py`, `config_manager.py`
85+
8486
Managers handle:
8587
- Input validation and error messages
8688
- Weaviate client API calls

.claude/skills/operating-weaviate-cli/SKILL.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ weaviate-cli [--config-file FILE] [--user USER] <group> <command> [--json] [opti
113113

114114
| Group | Description |
115115
|-------|-------------|
116-
| `create` | Create collections, tenants, data, backups, roles, users, aliases, replications |
117-
| `get` | Inspect collections, tenants, shards, backups, roles, users, nodes, aliases, replications |
116+
| `create` | Create collections, tenants, data, backups, exports, roles, users, aliases, replications |
117+
| `get` | Inspect collections, tenants, shards, backups, exports, roles, users, nodes, aliases, replications |
118118
| `update` | Update collections, tenants, shards, data, users, aliases |
119119
| `delete` | Delete collections, tenants, data, roles, users, aliases, replications |
120120
| `query` | Query data (fetch/vector/keyword/hybrid/uuid), replications, sharding state |
121121
| `restore` | Restore backups |
122-
| `cancel` | Cancel backups and replications |
122+
| `cancel` | Cancel backups, exports, and replications |
123123
| `assign` | Assign roles to users, permissions to roles |
124124
| `revoke` | Revoke roles from users, permissions from roles |
125125
| `benchmark` | Run QPS benchmarks |
@@ -220,6 +220,24 @@ Backends: `s3`, `gcs`, `filesystem`. Options: `--include`, `--exclude`, `--wait`
220220

221221
See [references/backups.md](references/backups.md).
222222

223+
### Collection Export
224+
225+
```bash
226+
weaviate-cli create export-collection --export_id my-export --backend s3 --file_format parquet --wait --json
227+
weaviate-cli create export-collection --export_id my-export --backend s3 --include "Movies,Books" --json
228+
weaviate-cli create export-collection --export_id my-export --backend s3 --exclude "TempData" --json
229+
weaviate-cli get export-collection --export_id my-export --backend s3 --json
230+
weaviate-cli cancel export-collection --export_id my-export --backend s3 --json
231+
```
232+
233+
Backends: `filesystem`, `s3`, `gcs`, `azure`. File formats: `parquet`.
234+
235+
Options: `--include`, `--exclude` (mutually exclusive), `--wait`
236+
237+
**Prerequisite**: The export backend must be configured on the Weaviate cluster (e.g., `COLLECTION_EXPORT=true` in local-k8s, which provisions MinIO and the `weaviate-export` bucket automatically).
238+
239+
See [references/exports.md](references/exports.md).
240+
223241
### RBAC (Roles, Users, Permissions)
224242

225243
```bash
@@ -363,6 +381,13 @@ hot/active <--> cold/inactive
363381
5. For timestamp-based TTL on existing collections: `--inverted_index timestamp` must be set at creation or already enabled
364382
6. For property-based TTL: the date property must exist, be `date` type, and have filterable or rangeable index
365383

384+
### Collection Export Workflow
385+
1. `create export-collection --backend s3 --export_id my-export --wait` -- create and wait for completion
386+
2. `get export-collection --backend s3 --export_id my-export` -- check status (includes shard-level progress)
387+
3. `cancel export-collection --backend s3 --export_id my-export` -- cancel in-progress export
388+
389+
**Prerequisite**: The export backend must be configured on the cluster. For local-k8s, deploy with `COLLECTION_EXPORT=true`, which provisions MinIO, creates the `weaviate-export` bucket, and wires `EXPORT_DEFAULT_BUCKET` automatically.
390+
366391
### Alias Workflow
367392
1. `create collection --collection Movies_v1` -- create the target collection
368393
2. `create alias Movies Movies_v1` -- create alias pointing to collection
@@ -417,6 +442,7 @@ When new commands or options are added to `weaviate-cli`:
417442
- [references/search.md](references/search.md) -- Search types, options, and selection guide
418443
- [references/tenants.md](references/tenants.md) -- Tenant state machine and management
419444
- [references/backups.md](references/backups.md) -- Backup/restore options and notes
445+
- [references/exports.md](references/exports.md) -- Collection export options and notes
420446
- [references/rbac.md](references/rbac.md) -- Permission format, actions, and examples
421447
- [references/cluster.md](references/cluster.md) -- Nodes, shards, replication operations
422448
- [references/benchmark.md](references/benchmark.md) -- Benchmark options and output modes
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Collection Export Reference
2+
3+
Export collections from Weaviate to external storage backends in Parquet format.
4+
5+
## Create Export
6+
```bash
7+
weaviate-cli create export-collection --export_id my-export --backend s3 --file_format parquet --wait --json
8+
weaviate-cli create export-collection --export_id my-export --backend s3 --include "Movies,Books" --json
9+
weaviate-cli create export-collection --export_id my-export --backend gcs --exclude "TempData" --json
10+
```
11+
12+
## Check Export Status
13+
```bash
14+
weaviate-cli get export-collection --export_id my-export --backend s3 --json
15+
```
16+
17+
Returns shard-level progress including objects exported per shard, errors, and timing.
18+
19+
## Cancel Export
20+
```bash
21+
weaviate-cli cancel export-collection --export_id my-export --backend s3 --json
22+
```
23+
24+
Only works while the export is in progress. Returns an error if the export has already completed.
25+
26+
## Options
27+
28+
**Create:**
29+
- `--export_id` -- Export identifier (default: "test-export")
30+
- `--backend` -- filesystem, s3, gcs, azure (default: filesystem)
31+
- `--file_format` -- Export format: parquet (default: parquet)
32+
- `--include` -- Comma-separated collections to include
33+
- `--exclude` -- Comma-separated collections to exclude
34+
- `--wait` -- Wait for completion
35+
36+
**Get Status:**
37+
- `--export_id`, `--backend` -- Same as create
38+
39+
**Cancel:**
40+
- `--export_id`, `--backend` -- Same as create
41+
42+
## Prerequisites
43+
44+
1. The export backend must be configured on the Weaviate cluster
45+
2. For local-k8s, deploy with `COLLECTION_EXPORT=true` (provisions MinIO, creates the `weaviate-export` bucket, and sets `EXPORT_DEFAULT_BUCKET`)
46+
3. `--include` and `--exclude` are mutually exclusive
47+
48+
## Notes
49+
50+
- `--wait` blocks until the export completes (SUCCESS, FAILED, or CANCELED)
51+
- Without `--wait`, the command returns immediately with status STARTED
52+
- Poll progress with `get export-collection` to monitor shard-level status
53+
- Export uses the same storage backends as backups (S3, GCS, Azure, filesystem)

.github/workflows/main.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,12 @@ jobs:
8282
weaviate-version: ${{ env.WEAVIATE_VERSION }}
8383
modules: ${{ env.MODULES }}
8484
enable-backup: true
85+
collection-export: true
8586
dynamic-users: true
8687
- name: Run integration tests with pytest
8788
run: |
8889
pip install pytest-html
89-
pytest test/integration/test_integration.py test/integration/test_data_integration.py test/integration/test_create_data_return_collection.py --html=test-report-${{ matrix.version }}.html --self-contained-html
90+
pytest test/integration/test_integration.py test/integration/test_data_integration.py test/integration/test_create_data_return_collection.py test/integration/test_export_integration.py --html=test-report-${{ matrix.version }}.html --self-contained-html
9091
integration-auth-tests:
9192
needs: [unit-tests, get-latest-weaviate-version]
9293
env:
@@ -111,6 +112,7 @@ jobs:
111112
weaviate-version: ${{ env.WEAVIATE_VERSION }}
112113
modules: ${{ env.MODULES }}
113114
enable-backup: true
115+
collection-export: true
114116
rbac: true
115117
dynamic-users: true
116118
- name: Create config directory

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
weaviate-client>=4.20.4
1+
weaviate-client>=4.21.0
22
click==8.1.7
33
twine
44
pytest

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ classifiers =
3737
include_package_data = True
3838
python_requires = >=3.9
3939
install_requires =
40-
weaviate-client>=4.20.4
40+
weaviate-client>=4.21.0
4141
click==8.1.7
4242
semver>=3.0.2
4343
numpy>=1.24.0
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import json
2+
import click
3+
import pytest
4+
import weaviate
5+
from weaviate_cli.managers.collection_manager import CollectionManager
6+
from weaviate_cli.managers.config_manager import ConfigManager
7+
from weaviate_cli.managers.data_manager import DataManager
8+
from weaviate_cli.managers.export_manager import ExportManager
9+
10+
11+
EXPORT_COLLECTION = "ExportTestCollection"
12+
13+
14+
@pytest.fixture
15+
def client() -> weaviate.WeaviateClient:
16+
config = ConfigManager()
17+
return config.get_client()
18+
19+
20+
@pytest.fixture
21+
def collection_manager(client: weaviate.WeaviateClient) -> CollectionManager:
22+
return CollectionManager(client)
23+
24+
25+
@pytest.fixture
26+
def data_manager(client: weaviate.WeaviateClient) -> DataManager:
27+
return DataManager(client)
28+
29+
30+
@pytest.fixture
31+
def export_manager(client: weaviate.WeaviateClient) -> ExportManager:
32+
return ExportManager(client)
33+
34+
35+
@pytest.fixture
36+
def setup_collection(collection_manager, data_manager):
37+
"""Create a collection with data for export tests."""
38+
try:
39+
collection_manager.create_collection(
40+
collection=EXPORT_COLLECTION,
41+
replication_factor=1,
42+
vectorizer="none",
43+
force_auto_schema=True,
44+
)
45+
data_manager.create_data(
46+
collection=EXPORT_COLLECTION,
47+
limit=100,
48+
randomize=True,
49+
consistency_level="one",
50+
)
51+
yield
52+
finally:
53+
if collection_manager.client.collections.exists(EXPORT_COLLECTION):
54+
collection_manager.delete_collection(collection=EXPORT_COLLECTION)
55+
56+
57+
def test_create_export_and_get_status(
58+
export_manager: ExportManager, setup_collection, capsys
59+
):
60+
"""Test creating an export and getting its status."""
61+
export_manager.create_export(
62+
export_id="integration-test-export",
63+
backend="s3",
64+
file_format="parquet",
65+
include=EXPORT_COLLECTION,
66+
wait=True,
67+
json_output=False,
68+
)
69+
70+
out = capsys.readouterr().out
71+
assert "integration-test-export" in out
72+
assert "created successfully" in out
73+
74+
export_manager.get_export_status(
75+
export_id="integration-test-export",
76+
backend="s3",
77+
json_output=True,
78+
)
79+
80+
out = capsys.readouterr().out
81+
data = json.loads(out)
82+
assert data["export_id"] == "integration-test-export"
83+
assert data["status"] == "SUCCESS"
84+
assert EXPORT_COLLECTION in data["collections"]
85+
assert "shard_status" in data
86+
87+
88+
def test_create_export_json_output(
89+
export_manager: ExportManager, setup_collection, capsys
90+
):
91+
"""Test creating an export with JSON output."""
92+
export_manager.create_export(
93+
export_id="integration-json-export",
94+
backend="s3",
95+
file_format="parquet",
96+
wait=True,
97+
json_output=True,
98+
)
99+
100+
out = capsys.readouterr().out
101+
data = json.loads(out)
102+
assert data["status"] == "SUCCESS"
103+
assert data["export_id"] == "integration-json-export"
104+
105+
106+
def test_create_export_with_exclude(
107+
export_manager: ExportManager,
108+
collection_manager: CollectionManager,
109+
data_manager: DataManager,
110+
setup_collection,
111+
capsys,
112+
):
113+
"""Test creating an export with exclude filter.
114+
115+
Creates a second collection so that excluding it still leaves
116+
EXPORT_COLLECTION exportable (the server rejects an export with no
117+
exportable classes).
118+
"""
119+
second_collection = "ExportTestCollection_Excluded"
120+
try:
121+
collection_manager.create_collection(
122+
collection=second_collection,
123+
replication_factor=1,
124+
vectorizer="none",
125+
force_auto_schema=True,
126+
)
127+
data_manager.create_data(
128+
collection=second_collection,
129+
limit=10,
130+
randomize=True,
131+
consistency_level="one",
132+
)
133+
capsys.readouterr() # Clear setup output
134+
135+
export_manager.create_export(
136+
export_id="integration-exclude-export",
137+
backend="s3",
138+
file_format="parquet",
139+
exclude=second_collection,
140+
wait=True,
141+
json_output=True,
142+
)
143+
144+
out = capsys.readouterr().out
145+
data = json.loads(out)
146+
assert data["status"] == "SUCCESS"
147+
assert second_collection not in data.get("collections", [])
148+
assert EXPORT_COLLECTION in data.get("collections", [])
149+
finally:
150+
if collection_manager.client.collections.exists(second_collection):
151+
collection_manager.delete_collection(collection=second_collection)
152+
153+
154+
def test_create_export_include_and_exclude_raises(
155+
export_manager: ExportManager, setup_collection
156+
):
157+
"""Test that specifying both include and exclude raises an error."""
158+
with pytest.raises(click.ClickException) as exc_info:
159+
export_manager.create_export(
160+
export_id="should-fail",
161+
backend="s3",
162+
file_format="parquet",
163+
include=EXPORT_COLLECTION,
164+
exclude="OtherCollection",
165+
)
166+
assert "include" in str(exc_info.value).lower()
167+
assert "exclude" in str(exc_info.value).lower()
168+
169+
170+
def test_cancel_export(export_manager: ExportManager, setup_collection, capsys):
171+
"""Test canceling an export."""
172+
# Create export without waiting
173+
export_manager.create_export(
174+
export_id="integration-cancel-export",
175+
backend="s3",
176+
file_format="parquet",
177+
wait=False,
178+
)
179+
capsys.readouterr() # Clear output
180+
181+
# Try to cancel — may succeed or fail depending on timing. Only tolerate
182+
# the specific "could not be canceled" path (export already finished);
183+
# anything else is a real failure.
184+
try:
185+
export_manager.cancel_export(
186+
export_id="integration-cancel-export",
187+
backend="s3",
188+
json_output=True,
189+
)
190+
except click.ClickException as e:
191+
assert "could not be canceled" in str(e)
192+
return
193+
194+
out = capsys.readouterr().out
195+
data = json.loads(out)
196+
assert data["status"] == "success"

0 commit comments

Comments
 (0)