diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 806ae5095..03b86c856 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -22,12 +22,13 @@ env: WEAVIATE_128: 1.28.16 WEAVIATE_129: 1.29.11 WEAVIATE_130: 1.30.22 - WEAVIATE_131: 1.31.22 - WEAVIATE_132: 1.32.26 - WEAVIATE_133: 1.33.11 - WEAVIATE_134: 1.34.8 - WEAVIATE_135: 1.35.2 - WEAVIATE_136: 1.36.0-dev-35d0c3a + WEAVIATE_131: 1.31.20 + WEAVIATE_132: 1.32.23 + WEAVIATE_133: 1.33.10 + WEAVIATE_134: 1.34.5 + WEAVIATE_135: 1.35.0 + WEAVIATE_136: 1.36.0-rc.0 + jobs: lint-and-format: diff --git a/integration/test_collection_config.py b/integration/test_collection_config.py index 1ed1df103..b545b99cb 100644 --- a/integration/test_collection_config.py +++ b/integration/test_collection_config.py @@ -39,6 +39,7 @@ Tokenization, _NamedVectorConfigCreate, _VectorizerConfigCreate, + IndexName, ) from weaviate.collections.classes.tenants import Tenant from weaviate.exceptions import UnexpectedStatusCodeError, WeaviateInvalidInputError @@ -1950,3 +1951,59 @@ def test_object_ttl_update(collection_factory: CollectionFactory) -> None: ) conf = collection.config.get() assert conf.object_ttl_config is None + + +@pytest.mark.parametrize("index_name", ["filterable", "searchable", "rangeFilters"]) +def test_delete_property_index( + index_name: IndexName, collection_factory: CollectionFactory +) -> None: + """Test delete index works for each index type.""" + collection_dummy = collection_factory("dummy") + if collection_dummy._connection._weaviate_version.is_lower_than(1, 36, 0): + pytest.skip("delete property index not supported before 1.36.0") + + if index_name == "filterable" or index_name == "searchable": + _data_type = DataType.TEXT + _index_range_filters = False + _index_searchable = True + _index_filterable = True + else: + _data_type = DataType.DATE + _index_range_filters = True + _index_searchable = False + _index_filterable = True + + collection = collection_factory( + properties=[ + Property( + name="indexed_prop", + data_type=_data_type, + index_range_filters=_index_range_filters, + index_searchable=_index_searchable, + index_filterable=_index_filterable, + ) + ], + ) + config = collection.config.get() + assert config.properties[0].index_filterable is _index_filterable + assert config.properties[0].index_searchable is _index_searchable + assert config.properties[0].index_range_filters is _index_range_filters + + with pytest.raises(weaviate.exceptions.UnexpectedStatusCodeError): + collection.config.delete_property_index("does_not_exist", index_name) + + collection.config.delete_property_index("indexed_prop", index_name) + + config = collection.config.get() + if index_name == "filterable": + assert config.properties[0].index_filterable is False + assert config.properties[0].index_searchable is _index_searchable + assert config.properties[0].index_range_filters is _index_range_filters + elif index_name == "searchable": + assert config.properties[0].index_searchable is False + assert config.properties[0].index_filterable is _index_filterable + assert config.properties[0].index_range_filters is _index_range_filters + elif index_name == "rangeFilters": + assert config.properties[0].index_range_filters is False + assert config.properties[0].index_searchable is _index_searchable + assert config.properties[0].index_filterable is _index_filterable diff --git a/weaviate/classes/config.py b/weaviate/classes/config.py index 651818de3..ce1faf993 100644 --- a/weaviate/classes/config.py +++ b/weaviate/classes/config.py @@ -3,6 +3,7 @@ ConsistencyLevel, DataType, GenerativeSearches, + IndexName, PQEncoderDistribution, PQEncoderType, Property, @@ -27,6 +28,7 @@ "Reconfigure", "DataType", "GenerativeSearches", + "IndexName", "Integrations", "Multi2VecField", "MultiVectorAggregation", diff --git a/weaviate/collections/classes/config.py b/weaviate/collections/classes/config.py index df79252b6..c4b679362 100644 --- a/weaviate/collections/classes/config.py +++ b/weaviate/collections/classes/config.py @@ -102,6 +102,12 @@ "high", ] +IndexName: TypeAlias = Literal[ + "searchable", + "filterable", + "rangeFilters", +] + class ConsistencyLevel(str, BaseEnum): """The consistency levels when writing to Weaviate with replication enabled. diff --git a/weaviate/collections/config/async_.pyi b/weaviate/collections/config/async_.pyi index 3b07f55c6..61ee09fdd 100644 --- a/weaviate/collections/config/async_.pyi +++ b/weaviate/collections/config/async_.pyi @@ -5,6 +5,7 @@ from typing_extensions import deprecated from weaviate.collections.classes.config import ( CollectionConfig, CollectionConfigSimple, + IndexName, Property, ReferenceProperty, ShardStatus, @@ -82,3 +83,4 @@ class _ConfigCollectionAsync(_ConfigCollectionExecutor[ConnectionAsync]): async def add_vector( self, *, vector_config: Union[_VectorConfigCreate, List[_VectorConfigCreate]] ) -> None: ... + async def delete_property_index(self, property_name: str, index_name: IndexName) -> bool: ... diff --git a/weaviate/collections/config/executor.py b/weaviate/collections/config/executor.py index bb1f33859..0a7bf1a49 100644 --- a/weaviate/collections/config/executor.py +++ b/weaviate/collections/config/executor.py @@ -19,6 +19,7 @@ from weaviate.collections.classes.config import ( CollectionConfig, CollectionConfigSimple, + IndexName, Property, PropertyType, ReferenceProperty, @@ -52,7 +53,11 @@ from weaviate.exceptions import ( WeaviateInvalidInputError, ) -from weaviate.util import _decode_json_response_dict, _decode_json_response_list +from weaviate.util import ( + _capitalize_first_letter, + _decode_json_response_dict, + _decode_json_response_list, +) from weaviate.validator import _validate_input, _ValidateArgument from weaviate.warnings import _Warnings @@ -581,3 +586,44 @@ async def _execute() -> None: return _execute() schema = executor.result(self.__get()) return executor.result(resp(schema)) + + def delete_property_index( + self, + property_name: str, + index_name: IndexName, + ) -> executor.Result[bool]: + """Delete a property index from the collection in Weaviate. + + This is a destructive operation. The index will + need to be regenerated if you wish to use it again. + + Args: + property_name: The property name from which to delete the index. + index_name: The type of the index to delete. + + Raises: + weaviate.exceptions.WeaviateConnectionError: If the network connection to Weaviate fails. + weaviate.exceptions.UnexpectedStatusCodeError: If Weaviate reports a non-OK status. + weaviate.exceptions.WeaviateInvalidInputError: If the property or index does not exist. + """ + _validate_input( + [_ValidateArgument(expected=[str], name="property_name", value=property_name)] + ) + _validate_input([_ValidateArgument(expected=[str], name="index_name", value=index_name)]) + + path = ( + f"/schema/{_capitalize_first_letter(self._name)}" + + f"/properties/{property_name}" + + f"/index/{index_name}" + ) + + def resp(res: Response) -> bool: + return res.status_code == 200 + + return executor.execute( + response_callback=resp, + method=self._connection.delete, + path=path, + error_msg="Property may not exist", + status_codes=_ExpectedStatusCodes(ok_in=[200], error="property exists"), + ) diff --git a/weaviate/collections/config/sync.pyi b/weaviate/collections/config/sync.pyi index 21fd705ac..8aafc32a2 100644 --- a/weaviate/collections/config/sync.pyi +++ b/weaviate/collections/config/sync.pyi @@ -5,6 +5,7 @@ from typing_extensions import deprecated from weaviate.collections.classes.config import ( CollectionConfig, CollectionConfigSimple, + IndexName, Property, ReferenceProperty, ShardStatus, @@ -80,3 +81,4 @@ class _ConfigCollection(_ConfigCollectionExecutor[ConnectionSync]): def add_vector( self, *, vector_config: Union[_VectorConfigCreate, List[_VectorConfigCreate]] ) -> None: ... + def delete_property_index(self, property_name: str, index_name: IndexName) -> bool: ...