Skip to content

Commit fc3375e

Browse files
committed
Simplify collection alias code.
Now it's possible to query schema using the alias so we don't need to keep the alias and collection referenced to do either one operation or the other. Also, add support for the override_alias when restoring the backup. Update the skills documentation with all the relevant changes.
1 parent 837a530 commit fc3375e

8 files changed

Lines changed: 149 additions & 54 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ Key query options: `--properties "title,keywords"`, `--tenants "T1"`, `--target_
183183

184184
**Note**: `--consistency_level` values must be **lowercase**: `one`, `quorum`, `all` (not `ONE`, `QUORUM`, `ALL`).
185185

186+
**Alias support**: The `--collection` flag accepts collection aliases in all data commands. If the name isn't a direct collection, the CLI checks the alias list automatically.
187+
186188
See [references/data.md](references/data.md) and [references/search.md](references/search.md).
187189

188190
### Tenants
@@ -213,7 +215,7 @@ weaviate-cli restore backup --backend s3 --backup_id my-backup --wait --json
213215
weaviate-cli cancel backup --backend s3 --backup_id my-backup --json
214216
```
215217

216-
Backends: `s3`, `gcs`, `filesystem`. Options: `--include`, `--exclude`, `--wait`, `--cpu_for_backup N`
218+
Backends: `s3`, `gcs`, `filesystem`. Options: `--include`, `--exclude`, `--wait`, `--cpu_for_backup N`, `--override-alias`
217219

218220
See [references/backups.md](references/backups.md).
219221

.claude/skills/operating-weaviate-cli/references/backups.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ weaviate-cli get backup --backend s3 --backup_id my-backup --restore --json
2323
```bash
2424
weaviate-cli restore backup --backend s3 --backup_id my-backup --wait --json
2525
weaviate-cli restore backup --backend s3 --backup_id my-backup --include "Movies" --wait --json
26+
weaviate-cli restore backup --backend s3 --backup_id my-backup --override-alias --wait --json
2627
```
2728

2829
## Cancel Backup
@@ -44,6 +45,7 @@ weaviate-cli cancel backup --backend s3 --backup_id my-backup --json
4445
- `--backend`, `--backup_id` -- Same as create
4546
- `--include`, `--exclude` -- Filter which collections to restore
4647
- `--wait` -- Wait for completion
48+
- `--override-alias` -- Override alias conflicts during restore. Use when the backup contains a collection with an alias that differs from the current cluster state (e.g., the backup had alias `Movies` pointing to `Movies_v1`, but the cluster now has `Movies` pointing to `Movies_v2`). Without this flag, the restore will fail due to the alias conflict.
4749

4850
## Prerequisites
4951

.claude/skills/operating-weaviate-cli/references/data.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ weaviate-cli delete data --collection "Movies" --tenants "T1,T2" --limit 50 --js
5050
3. **For multi-tenant data ingestion:** tenants must be in `hot`/`active` state
5151
4. **For vectorizer-based ingestion (non-randomize):** the collection uses a built-in dataset; vectorizer API keys may be needed
5252

53+
## Using Aliases
54+
55+
The `--collection` flag accepts aliases in all data commands (`create data`, `query data`, `update data`, `delete data`). If the given name doesn't match a collection directly, the CLI checks the alias list and resolves it transparently.
56+
57+
```bash
58+
# Create an alias, then use it for data operations
59+
weaviate-cli create alias MyAlias Movies --json
60+
weaviate-cli create data --collection MyAlias --limit 100 --randomize --json
61+
weaviate-cli query data --collection MyAlias --search_type fetch --limit 10 --json
62+
weaviate-cli update data --collection MyAlias --limit 50 --randomize --json
63+
weaviate-cli delete data --collection MyAlias --limit 50 --json
64+
```
65+
5366
## Notes
5467

5568
- `--randomize` generates synthetic data with random vectors -- useful for testing

test/unittests/test_managers/test_data_manager.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,118 @@ def test_fallback_no_tenants_with_auto_creation_returns_empty(self) -> None:
223223
assert result == []
224224

225225

226+
# ---------------------------------------------------------------------------
227+
# Alias resolution in data operations
228+
# ---------------------------------------------------------------------------
229+
230+
231+
class TestAliasResolution:
232+
"""Unit tests for alias resolution in create/update/delete/query data."""
233+
234+
def _setup_alias_mock(self, mock_client, alias_name="MyAlias"):
235+
"""Set up a mock client where collection doesn't exist but alias does."""
236+
mock_collections = MagicMock()
237+
mock_collections.exists.return_value = False
238+
mock_client.collections = mock_collections
239+
240+
mock_alias = MagicMock()
241+
mock_alias.list_all.return_value = {alias_name: MagicMock()}
242+
mock_client.alias = mock_alias
243+
244+
mock_collection = MagicMock()
245+
mock_collection.config.get.return_value = MagicMock(
246+
multi_tenancy_config=MagicMock(
247+
enabled=False,
248+
auto_tenant_creation=False,
249+
auto_tenant_activation=False,
250+
)
251+
)
252+
mock_client.collections.get.return_value = mock_collection
253+
return mock_collection
254+
255+
def test_create_data_with_alias(self, mock_client):
256+
self._setup_alias_mock(mock_client)
257+
manager = DataManager(mock_client)
258+
manager.create_data(collection="MyAlias", limit=10, randomize=True)
259+
mock_client.collections.get.assert_called_once_with("MyAlias")
260+
261+
def test_update_data_with_alias(self, mock_client):
262+
self._setup_alias_mock(mock_client)
263+
manager = DataManager(mock_client)
264+
manager.update_data(collection="MyAlias", limit=10, randomize=True)
265+
mock_client.collections.get.assert_called_once_with("MyAlias")
266+
267+
def test_delete_data_with_alias(self, mock_client):
268+
col = self._setup_alias_mock(mock_client)
269+
col.config.get.return_value.multi_tenancy_config.enabled = False
270+
# delete_data iterates objects, mock the iterator
271+
col.iterator.return_value = iter([])
272+
manager = DataManager(mock_client)
273+
manager.delete_data(collection="MyAlias", limit=10)
274+
mock_client.collections.get.assert_called_once_with("MyAlias")
275+
276+
def test_query_data_with_alias(self, mock_client):
277+
col = self._setup_alias_mock(mock_client)
278+
col.config.get.return_value.multi_tenancy_config.enabled = False
279+
col.query.fetch_objects.return_value = MagicMock(objects=[])
280+
manager = DataManager(mock_client)
281+
manager.query_data(collection="MyAlias", search_type="fetch", limit=5)
282+
mock_client.collections.get.assert_called_once_with("MyAlias")
283+
284+
def _setup_not_found_mock(self, mock_client):
285+
"""Set up a mock client where neither collection nor alias exists."""
286+
mock_collections = MagicMock()
287+
mock_collections.exists.return_value = False
288+
mock_client.collections = mock_collections
289+
mock_alias = MagicMock()
290+
mock_alias.list_all.return_value = {}
291+
mock_client.alias = mock_alias
292+
293+
def test_create_data_not_collection_not_alias_raises(self, mock_client):
294+
self._setup_not_found_mock(mock_client)
295+
manager = DataManager(mock_client)
296+
with pytest.raises(Exception, match="does not exist"):
297+
manager.create_data(collection="NonExistent", limit=10, randomize=True)
298+
299+
def test_update_data_not_collection_not_alias_raises(self, mock_client):
300+
self._setup_not_found_mock(mock_client)
301+
manager = DataManager(mock_client)
302+
with pytest.raises(Exception, match="does not exist"):
303+
manager.update_data(collection="NonExistent", limit=10, randomize=True)
304+
305+
def test_delete_data_not_collection_not_alias_raises(self, mock_client):
306+
self._setup_not_found_mock(mock_client)
307+
manager = DataManager(mock_client)
308+
with pytest.raises(Exception, match="does not exist"):
309+
manager.delete_data(collection="NonExistent", limit=10)
310+
311+
def test_query_data_not_collection_not_alias_raises(self, mock_client):
312+
self._setup_not_found_mock(mock_client)
313+
manager = DataManager(mock_client)
314+
with pytest.raises(Exception, match="does not exist"):
315+
manager.query_data(collection="NonExistent", search_type="fetch", limit=5)
316+
317+
def test_create_data_direct_collection_skips_alias_check(self, mock_client):
318+
"""When collection exists directly, alias.list_all should not be called."""
319+
mock_collections = MagicMock()
320+
mock_collections.exists.return_value = True
321+
mock_client.collections = mock_collections
322+
mock_alias = MagicMock()
323+
mock_client.alias = mock_alias
324+
mock_collection = MagicMock()
325+
mock_collection.config.get.return_value = MagicMock(
326+
multi_tenancy_config=MagicMock(
327+
enabled=False,
328+
auto_tenant_creation=False,
329+
auto_tenant_activation=False,
330+
)
331+
)
332+
mock_client.collections.get.return_value = mock_collection
333+
manager = DataManager(mock_client)
334+
manager.create_data(collection="DirectCollection", limit=10, randomize=True)
335+
mock_client.alias.list_all.assert_not_called()
336+
337+
226338
def test_ingest_data(mock_client):
227339
manager = DataManager(mock_client)
228340
mock_collections = MagicMock()

weaviate_cli/commands/restore.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,18 @@ def restore() -> None:
3838
default=RestoreBackupDefaults.exclude,
3939
help="Collection to exclude in backup (default: None).",
4040
)
41+
@click.option(
42+
"--override-alias",
43+
is_flag=True,
44+
help="Override the alias of the collection (default: False).",
45+
)
4146
@click.option(
4247
"--json", "json_output", is_flag=True, default=False, help="Output in JSON format."
4348
)
4449
@click.pass_context
45-
def restore_backup_cli(ctx, backend, include, exclude, backup_id, wait, json_output):
50+
def restore_backup_cli(
51+
ctx, backend, include, exclude, backup_id, wait, override_alias, json_output
52+
):
4653
"""Restore a backup in Weaviate."""
4754

4855
client = None
@@ -55,6 +62,7 @@ def restore_backup_cli(ctx, backend, include, exclude, backup_id, wait, json_out
5562
include=include,
5663
exclude=exclude,
5764
wait=wait,
65+
override_alias=override_alias,
5866
json_output=json_output,
5967
)
6068
except Exception as e:

weaviate_cli/defaults.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ class RestoreBackupDefaults:
237237
wait: bool = False
238238
include: Optional[str] = None
239239
exclude: Optional[str] = None
240+
override_alias: bool = False
240241

241242

242243
@dataclass

weaviate_cli/managers/backup_manager.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
class BackupManager:
1616
def __init__(self, client: WeaviateClient) -> None:
17-
self.client = client
17+
self.client: WeaviateClient = client
1818

1919
def create_backup(
2020
self,
@@ -81,6 +81,7 @@ def restore_backup(
8181
include: Optional[str] = RestoreBackupDefaults.include,
8282
exclude: Optional[str] = RestoreBackupDefaults.exclude,
8383
wait: bool = RestoreBackupDefaults.wait,
84+
override_alias: bool = RestoreBackupDefaults.override_alias,
8485
json_output: bool = False,
8586
) -> None:
8687

@@ -89,6 +90,7 @@ def restore_backup(
8990
backend=backend,
9091
include_collections=include.split(",") if include else None,
9192
exclude_collections=exclude.split(",") if exclude else None,
93+
overwrite_alias=override_alias,
9294
wait_for_completion=wait,
9395
)
9496

0 commit comments

Comments
 (0)