diff --git a/haystack/utils/filters.py b/haystack/utils/filters.py index 5d89e9eefab..6fb9afcc129 100644 --- a/haystack/utils/filters.py +++ b/haystack/utils/filters.py @@ -48,6 +48,10 @@ def _not(document: Document | ByteStream, conditions: list[dict[str, Any]]) -> b LOGICAL_OPERATORS = {"NOT": _not, "OR": _or, "AND": _and} +def _valid_operators_message(operators: dict[str, Any]) -> str: + return ", ".join(f"'{operator}'" for operator in sorted(operators)) + + def _equal(value: Any, filter_value: Any) -> bool: return value == filter_value @@ -173,6 +177,12 @@ def _logic_condition(condition: dict[str, Any], document: Document | ByteStream) msg = f"'conditions' key missing in {condition}" raise FilterError(msg) operator: str = condition["operator"] + if operator not in LOGICAL_OPERATORS: + msg = ( + f"Unsupported logical operator '{operator}'. " + f"Valid operators are: {_valid_operators_message(LOGICAL_OPERATORS)}." + ) + raise FilterError(msg) conditions: list[dict[str, Any]] = condition["conditions"] return LOGICAL_OPERATORS[operator](document=document, conditions=conditions) @@ -214,5 +224,11 @@ def _comparison_condition(condition: dict[str, Any], document: Document | ByteSt else: document_value = getattr(document, field) operator: str = condition["operator"] + if operator not in COMPARISON_OPERATORS: + msg = ( + f"Unsupported comparison operator '{operator}'. " + f"Valid operators are: {_valid_operators_message(COMPARISON_OPERATORS)}." + ) + raise FilterError(msg) filter_value: Any = condition["value"] return COMPARISON_OPERATORS[operator](filter_value=filter_value, value=document_value) diff --git a/test/utils/test_filters.py b/test/utils/test_filters.py index f5fbf66d6a8..c717cff5981 100644 --- a/test/utils/test_filters.py +++ b/test/utils/test_filters.py @@ -574,3 +574,32 @@ def test_document_matches_filter_raises_error(filters): with pytest.raises(FilterError): document = Document(meta={"page": 10}) document_matches_filter(filters, document) + + +@pytest.mark.parametrize( + "filters, error_message, expected_valid_operators", + [ + pytest.param( + {"field": "meta.page", "operator": "gt", "value": 1}, + "Unsupported comparison operator 'gt'", + ["'=='", "'!='", "'>'", "'>='", "'<'", "'<='", "'in'", "'not in'"], + id="unknown comparison operator", + ), + pytest.param( + {"operator": "XOR", "conditions": [{"field": "meta.page", "operator": "==", "value": 1}]}, + "Unsupported logical operator 'XOR'", + ["'AND'", "'OR'", "'NOT'"], + id="unknown logical operator", + ), + ], +) +def test_document_matches_filter_raises_helpful_error_for_unknown_operator( + filters, error_message, expected_valid_operators +): + with pytest.raises(FilterError) as exc_info: + document_matches_filter(filters, Document(meta={"page": 10})) + + message = str(exc_info.value) + assert error_message in message + for operator in expected_valid_operators: + assert operator in message