diff --git a/test/collection/test_filter.py b/test/collection/test_filter.py index 54c11abc5..65be332b2 100644 --- a/test/collection/test_filter.py +++ b/test/collection/test_filter.py @@ -26,6 +26,36 @@ def test_empty_input_contains_all() -> None: wvc.query.Filter.by_property("test").contains_all([]) +def test_empty_list_equal() -> None: + with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError): + wvc.query.Filter.by_property("test").equal([]) + + +def test_empty_list_not_equal() -> None: + with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError): + wvc.query.Filter.by_property("test").not_equal([]) + + +def test_empty_list_less_than() -> None: + with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError): + wvc.query.Filter.by_property("test").less_than([]) + + +def test_empty_list_less_or_equal() -> None: + with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError): + wvc.query.Filter.by_property("test").less_or_equal([]) + + +def test_empty_list_greater_than() -> None: + with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError): + wvc.query.Filter.by_property("test").greater_than([]) + + +def test_empty_list_greater_or_equal() -> None: + with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError): + wvc.query.Filter.by_property("test").greater_or_equal([]) + + def test_filter_lists() -> None: f1 = wvc.query.Filter.by_property("test").equal("test") f2 = wvc.query.Filter.by_creation_time().greater_or_equal(datetime.datetime.now()) diff --git a/weaviate/collections/classes/filters.py b/weaviate/collections/classes/filters.py index cbeac3aa2..a05d9eab2 100644 --- a/weaviate/collections/classes/filters.py +++ b/weaviate/collections/classes/filters.py @@ -220,18 +220,42 @@ def contains_none(self, val: FilterValuesList) -> _Filters: def equal(self, val: FilterValues) -> _Filters: """Filter on whether the property is equal to the given value.""" + if isinstance(val, list) and len(val) == 0: + raise WeaviateInvalidInputError( + "Filtering on empty lists is not supported by Weaviate. " + "To filter by property length, use " + "Filter.by_property('prop', length=True).equal(0)" + ) return _FilterValue(target=self._target_path(), value=val, operator=_Operator.EQUAL) def not_equal(self, val: FilterValues) -> _Filters: """Filter on whether the property is not equal to the given value.""" + if isinstance(val, list) and len(val) == 0: + raise WeaviateInvalidInputError( + "Filtering on empty lists is not supported by Weaviate. " + "To filter by property length, use " + "Filter.by_property('prop', length=True).equal(0)" + ) return _FilterValue(target=self._target_path(), value=val, operator=_Operator.NOT_EQUAL) def less_than(self, val: FilterValues) -> _Filters: """Filter on whether the property is less than the given value.""" + if isinstance(val, list) and len(val) == 0: + raise WeaviateInvalidInputError( + "Filtering on empty lists is not supported by Weaviate. " + "To filter by property length, use " + "Filter.by_property('prop', length=True).equal(0)" + ) return _FilterValue(target=self._target_path(), value=val, operator=_Operator.LESS_THAN) def less_or_equal(self, val: FilterValues) -> _Filters: """Filter on whether the property is less than or equal to the given value.""" + if isinstance(val, list) and len(val) == 0: + raise WeaviateInvalidInputError( + "Filtering on empty lists is not supported by Weaviate. " + "To filter by property length, use " + "Filter.by_property('prop', length=True).equal(0)" + ) return _FilterValue( target=self._target_path(), value=val, @@ -240,6 +264,12 @@ def less_or_equal(self, val: FilterValues) -> _Filters: def greater_than(self, val: FilterValues) -> _Filters: """Filter on whether the property is greater than the given value.""" + if isinstance(val, list) and len(val) == 0: + raise WeaviateInvalidInputError( + "Filtering on empty lists is not supported by Weaviate. " + "To filter by property length, use " + "Filter.by_property('prop', length=True).equal(0)" + ) return _FilterValue( target=self._target_path(), value=val, @@ -248,6 +278,12 @@ def greater_than(self, val: FilterValues) -> _Filters: def greater_or_equal(self, val: FilterValues) -> _Filters: """Filter on whether the property is greater than or equal to the given value.""" + if isinstance(val, list) and len(val) == 0: + raise WeaviateInvalidInputError( + "Filtering on empty lists is not supported by Weaviate. " + "To filter by property length, use " + "Filter.by_property('prop', length=True).equal(0)" + ) return _FilterValue( target=self._target_path(), value=val, diff --git a/weaviate/collections/filters.py b/weaviate/collections/filters.py index 41aebf95c..49d847c02 100644 --- a/weaviate/collections/filters.py +++ b/weaviate/collections/filters.py @@ -116,7 +116,9 @@ def __filter_to_text(value: FilterValues) -> Optional[str]: @staticmethod def __filter_to_text_list(value: FilterValues) -> Optional[base_pb2.TextArray]: - if not isinstance(value, list) or not ( + if not isinstance(value, list) or len(value) == 0: + return None + if not ( isinstance(value[0], TIME) or isinstance(value[0], str) or isinstance(value[0], uuid_lib.UUID) @@ -135,14 +137,14 @@ def __filter_to_text_list(value: FilterValues) -> Optional[base_pb2.TextArray]: @staticmethod def __filter_to_bool_list(value: FilterValues) -> Optional[base_pb2.BooleanArray]: - if not isinstance(value, list) or not isinstance(value[0], bool): + if not isinstance(value, list) or len(value) == 0 or not isinstance(value[0], bool): return None return base_pb2.BooleanArray(values=cast(List[bool], value)) @staticmethod def __filter_to_float_list(value: FilterValues) -> Optional[base_pb2.NumberArray]: - if not isinstance(value, list) or not isinstance(value[0], float): + if not isinstance(value, list) or len(value) == 0 or not isinstance(value[0], float): return None return base_pb2.NumberArray(values=cast(List[float], value)) @@ -152,6 +154,7 @@ def __filter_to_int_list(value: FilterValues) -> Optional[base_pb2.IntArray]: # bool is a subclass of int in Python, so the check must ensure it's not a bool if ( not isinstance(value, list) + or len(value) == 0 or not isinstance(value[0], int) or isinstance(value[0], bool) ): @@ -224,6 +227,12 @@ def __parse_filter(value: FilterValues) -> Dict[str, Any]: if isinstance(value, float): return {"valueNumber": value} if isinstance(value, list): + if len(value) == 0: + raise WeaviateInvalidInputError( + "Filtering on empty lists is not supported by Weaviate. " + "To filter by property length, use " + "Filter.by_property('prop', length=True).equal(0)" + ) if isinstance(value[0], str): return {"valueTextArray": value} if isinstance(value[0], uuid_lib.UUID):