|
1 | 1 | import datetime |
2 | | -from typing import Generator, List, Optional, Union |
| 2 | +import time |
| 3 | +from typing import Any, Dict, Generator, List, Optional, Union |
3 | 4 |
|
4 | 5 | import pytest as pytest |
5 | 6 | from _pytest.fixtures import SubRequest |
|
11 | 12 | OpenAICollection, |
12 | 13 | _sanitize_collection_name, |
13 | 14 | ) |
| 15 | +from weaviate.collections import Collection |
14 | 16 | from weaviate.collections.classes.config import ( |
15 | 17 | _BQConfig, |
16 | 18 | _CollectionConfig, |
|
37 | 39 | Rerankers, |
38 | 40 | _RerankerProvider, |
39 | 41 | Tokenization, |
| 42 | + _NamedVectorConfig, |
40 | 43 | _NamedVectorConfigCreate, |
41 | 44 | _VectorizerConfigCreate, |
42 | 45 | IndexName, |
@@ -2694,3 +2697,77 @@ def test_text_analyzer_roundtrip_from_dict( |
2694 | 2697 | assert config == new |
2695 | 2698 | assert config.to_dict() == new.to_dict() |
2696 | 2699 | client.collections.delete(name) |
| 2700 | + |
| 2701 | + |
| 2702 | +def _vector_config_without_index( |
| 2703 | + collection: Collection[Any, Any], vector_name: str, timeout: float = 30 |
| 2704 | +) -> Dict[str, _NamedVectorConfig]: |
| 2705 | + """Poll the collection config until `vector_name` no longer has an index. |
| 2706 | +
|
| 2707 | + The drop is applied asynchronously, a 200 from the endpoint only means that Weaviate accepted |
| 2708 | + the request. It then becomes visible in two steps: the vector first stays in the schema with a |
| 2709 | + `vector_index_config` of `None`, and once the index is gone from disk the entry is removed from |
| 2710 | + the schema altogether. Both shapes must parse, so accept either. |
| 2711 | + """ |
| 2712 | + start = time.time() |
| 2713 | + while True: |
| 2714 | + vector_config = collection.config.get().vector_config |
| 2715 | + assert vector_config is not None |
| 2716 | + if ( |
| 2717 | + vector_name not in vector_config |
| 2718 | + or vector_config[vector_name].vector_index_config is None |
| 2719 | + ): |
| 2720 | + return vector_config |
| 2721 | + if time.time() - start > timeout: |
| 2722 | + pytest.fail(f"vector index of {vector_name} was not dropped within {timeout}s") |
| 2723 | + time.sleep(0.2) |
| 2724 | + |
| 2725 | + |
| 2726 | +def test_delete_vector_index(collection_factory: CollectionFactory) -> None: |
| 2727 | + """Test that dropping the index of a named vector leaves the rest of the collection usable.""" |
| 2728 | + collection_dummy = collection_factory("dummy") |
| 2729 | + if collection_dummy._connection._weaviate_version.is_lower_than(1, 39, 0): |
| 2730 | + pytest.skip("delete vector index not supported before 1.39.0") |
| 2731 | + |
| 2732 | + collection = collection_factory( |
| 2733 | + properties=[Property(name="name", data_type=DataType.TEXT)], |
| 2734 | + vector_config=[ |
| 2735 | + Configure.Vectors.self_provided(name="dropped"), |
| 2736 | + Configure.Vectors.self_provided(name="kept"), |
| 2737 | + ], |
| 2738 | + ) |
| 2739 | + collection.data.insert( |
| 2740 | + properties={"name": "banana"}, |
| 2741 | + vector={"dropped": [1, 2], "kept": [3, 4]}, |
| 2742 | + ) |
| 2743 | + |
| 2744 | + config = collection.config.get() |
| 2745 | + assert config.vector_config is not None |
| 2746 | + assert config.vector_config["dropped"].vector_index_config is not None |
| 2747 | + |
| 2748 | + with pytest.raises(weaviate.exceptions.UnexpectedStatusCodeError): |
| 2749 | + collection.config.delete_vector_index("does_not_exist") |
| 2750 | + |
| 2751 | + assert collection.config.delete_vector_index("dropped") is True |
| 2752 | + |
| 2753 | + vector_config = _vector_config_without_index(collection, "dropped") |
| 2754 | + # vectors that were not dropped keep their index |
| 2755 | + assert vector_config["kept"].vector_index_config is not None |
| 2756 | + |
| 2757 | + # `list_all` parses the schema through the simple config parser, it must cope with the drop too |
| 2758 | + with weaviate.connect_to_local() as client: |
| 2759 | + simple = client.collections.list_all()[collection.name] |
| 2760 | + assert simple.vector_config is not None |
| 2761 | + assert simple.vector_config["kept"].vector_index_config is not None |
| 2762 | + |
| 2763 | + # searching the vector that still has an index keeps working |
| 2764 | + assert len(collection.query.near_vector([3, 4], target_vector="kept").objects) == 1 |
| 2765 | + |
| 2766 | + |
| 2767 | +def test_delete_vector_index_invalid_input(collection_factory: CollectionFactory) -> None: |
| 2768 | + """Test that a non-string vector name is rejected before hitting the network.""" |
| 2769 | + collection = collection_factory( |
| 2770 | + vector_config=[Configure.Vectors.self_provided(name="vec")], |
| 2771 | + ) |
| 2772 | + with pytest.raises(WeaviateInvalidInputError): |
| 2773 | + collection.config.delete_vector_index(42) # type: ignore[arg-type] |
0 commit comments