Skip to content

Commit 146ab23

Browse files
authored
Merge pull request #1962 from weaviate/pr/1942
Fix empty list filter raising IndexError instead of WeaviateInvalidInputError
2 parents ed3eed2 + 460b20b commit 146ab23

File tree

4 files changed

+94
-4
lines changed

4 files changed

+94
-4
lines changed

integration/test_rbac.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@
389389
(
390390
Permissions.alias(alias="myCAR", collection="*", read=True, delete=True),
391391
Role(
392-
name="AlliasRole",
392+
name="AlliasRole3",
393393
alias_permissions=[
394394
AliasPermissionOutput(
395395
alias="MyCAR", # capitalized the first letter.

test/collection/test_filter.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
_FilterAnd,
1010
_FilterNot,
1111
_FilterOr,
12+
_FilterValue,
1213
_Operator,
1314
)
15+
from weaviate.collections.filters import _FilterToGRPC
1416
from weaviate.proto.v1 import base_pb2
1517

1618

@@ -26,6 +28,43 @@ def test_empty_input_contains_all() -> None:
2628
wvc.query.Filter.by_property("test").contains_all([])
2729

2830

31+
def test_empty_list_equal() -> None:
32+
with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError):
33+
wvc.query.Filter.by_property("test").equal([])
34+
35+
36+
def test_empty_list_not_equal() -> None:
37+
with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError):
38+
wvc.query.Filter.by_property("test").not_equal([])
39+
40+
41+
def test_empty_list_less_than() -> None:
42+
with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError):
43+
wvc.query.Filter.by_property("test").less_than([])
44+
45+
46+
def test_empty_list_less_or_equal() -> None:
47+
with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError):
48+
wvc.query.Filter.by_property("test").less_or_equal([])
49+
50+
51+
def test_empty_list_greater_than() -> None:
52+
with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError):
53+
wvc.query.Filter.by_property("test").greater_than([])
54+
55+
56+
def test_empty_list_greater_or_equal() -> None:
57+
with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError):
58+
wvc.query.Filter.by_property("test").greater_or_equal([])
59+
60+
61+
def test_empty_list_grpc_conversion() -> None:
62+
"""Ensure the gRPC converter raises WeaviateInvalidInputError for empty lists."""
63+
fv = _FilterValue(target="test", value=[], operator=_Operator.EQUAL)
64+
with pytest.raises(weaviate.exceptions.WeaviateInvalidInputError):
65+
_FilterToGRPC.convert(fv)
66+
67+
2968
def test_filter_lists() -> None:
3069
f1 = wvc.query.Filter.by_property("test").equal("test")
3170
f2 = wvc.query.Filter.by_creation_time().greater_or_equal(datetime.datetime.now())

weaviate/collections/classes/filters.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,18 +220,42 @@ def contains_none(self, val: FilterValuesList) -> _Filters:
220220

221221
def equal(self, val: FilterValues) -> _Filters:
222222
"""Filter on whether the property is equal to the given value."""
223+
if isinstance(val, list) and len(val) == 0:
224+
raise WeaviateInvalidInputError(
225+
"Filtering on empty lists is not supported by Weaviate. "
226+
"To filter by property length, use "
227+
"Filter.by_property('prop', length=True).equal(0)"
228+
)
223229
return _FilterValue(target=self._target_path(), value=val, operator=_Operator.EQUAL)
224230

225231
def not_equal(self, val: FilterValues) -> _Filters:
226232
"""Filter on whether the property is not equal to the given value."""
233+
if isinstance(val, list) and len(val) == 0:
234+
raise WeaviateInvalidInputError(
235+
"Filtering on empty lists is not supported by Weaviate. "
236+
"To filter by property length, use "
237+
"Filter.by_property('prop', length=True).equal(0)"
238+
)
227239
return _FilterValue(target=self._target_path(), value=val, operator=_Operator.NOT_EQUAL)
228240

229241
def less_than(self, val: FilterValues) -> _Filters:
230242
"""Filter on whether the property is less than the given value."""
243+
if isinstance(val, list) and len(val) == 0:
244+
raise WeaviateInvalidInputError(
245+
"Filtering on empty lists is not supported by Weaviate. "
246+
"To filter by property length, use "
247+
"Filter.by_property('prop', length=True).equal(0)"
248+
)
231249
return _FilterValue(target=self._target_path(), value=val, operator=_Operator.LESS_THAN)
232250

233251
def less_or_equal(self, val: FilterValues) -> _Filters:
234252
"""Filter on whether the property is less than or equal to the given value."""
253+
if isinstance(val, list) and len(val) == 0:
254+
raise WeaviateInvalidInputError(
255+
"Filtering on empty lists is not supported by Weaviate. "
256+
"To filter by property length, use "
257+
"Filter.by_property('prop', length=True).equal(0)"
258+
)
235259
return _FilterValue(
236260
target=self._target_path(),
237261
value=val,
@@ -240,6 +264,12 @@ def less_or_equal(self, val: FilterValues) -> _Filters:
240264

241265
def greater_than(self, val: FilterValues) -> _Filters:
242266
"""Filter on whether the property is greater than the given value."""
267+
if isinstance(val, list) and len(val) == 0:
268+
raise WeaviateInvalidInputError(
269+
"Filtering on empty lists is not supported by Weaviate. "
270+
"To filter by property length, use "
271+
"Filter.by_property('prop', length=True).equal(0)"
272+
)
243273
return _FilterValue(
244274
target=self._target_path(),
245275
value=val,
@@ -248,6 +278,12 @@ def greater_than(self, val: FilterValues) -> _Filters:
248278

249279
def greater_or_equal(self, val: FilterValues) -> _Filters:
250280
"""Filter on whether the property is greater than or equal to the given value."""
281+
if isinstance(val, list) and len(val) == 0:
282+
raise WeaviateInvalidInputError(
283+
"Filtering on empty lists is not supported by Weaviate. "
284+
"To filter by property length, use "
285+
"Filter.by_property('prop', length=True).equal(0)"
286+
)
251287
return _FilterValue(
252288
target=self._target_path(),
253289
value=val,

weaviate/collections/filters.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ def convert(weav_filter: Optional[_Filters]) -> Optional[base_pb2.Filters]:
4040

4141
@staticmethod
4242
def __value_filter(weav_filter: _FilterValue) -> base_pb2.Filters:
43+
if isinstance(weav_filter.value, list) and len(weav_filter.value) == 0:
44+
raise WeaviateInvalidInputError(
45+
"Filtering on empty lists is not supported by Weaviate. "
46+
"To filter by property length, use "
47+
"Filter.by_property('prop', length=True).equal(0)"
48+
)
4349
operator = weav_filter.operator._to_grpc()
4450
target = _FilterToGRPC.__to_target(weav_filter.target)
4551
if isinstance(weav_filter.value, bool):
@@ -116,7 +122,9 @@ def __filter_to_text(value: FilterValues) -> Optional[str]:
116122

117123
@staticmethod
118124
def __filter_to_text_list(value: FilterValues) -> Optional[base_pb2.TextArray]:
119-
if not isinstance(value, list) or not (
125+
if not isinstance(value, list) or len(value) == 0:
126+
return None
127+
if not (
120128
isinstance(value[0], TIME)
121129
or isinstance(value[0], str)
122130
or isinstance(value[0], uuid_lib.UUID)
@@ -135,14 +143,14 @@ def __filter_to_text_list(value: FilterValues) -> Optional[base_pb2.TextArray]:
135143

136144
@staticmethod
137145
def __filter_to_bool_list(value: FilterValues) -> Optional[base_pb2.BooleanArray]:
138-
if not isinstance(value, list) or not isinstance(value[0], bool):
146+
if not isinstance(value, list) or len(value) == 0 or not isinstance(value[0], bool):
139147
return None
140148

141149
return base_pb2.BooleanArray(values=cast(List[bool], value))
142150

143151
@staticmethod
144152
def __filter_to_float_list(value: FilterValues) -> Optional[base_pb2.NumberArray]:
145-
if not isinstance(value, list) or not isinstance(value[0], float):
153+
if not isinstance(value, list) or len(value) == 0 or not isinstance(value[0], float):
146154
return None
147155

148156
return base_pb2.NumberArray(values=cast(List[float], value))
@@ -152,6 +160,7 @@ def __filter_to_int_list(value: FilterValues) -> Optional[base_pb2.IntArray]:
152160
# bool is a subclass of int in Python, so the check must ensure it's not a bool
153161
if (
154162
not isinstance(value, list)
163+
or len(value) == 0
155164
or not isinstance(value[0], int)
156165
or isinstance(value[0], bool)
157166
):
@@ -224,6 +233,12 @@ def __parse_filter(value: FilterValues) -> Dict[str, Any]:
224233
if isinstance(value, float):
225234
return {"valueNumber": value}
226235
if isinstance(value, list):
236+
if len(value) == 0:
237+
raise WeaviateInvalidInputError(
238+
"Filtering on empty lists is not supported by Weaviate. "
239+
"To filter by property length, use "
240+
"Filter.by_property('prop', length=True).equal(0)"
241+
)
227242
if isinstance(value[0], str):
228243
return {"valueTextArray": value}
229244
if isinstance(value[0], uuid_lib.UUID):

0 commit comments

Comments
 (0)