Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions haystack/utils/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
29 changes: 29 additions & 0 deletions test/utils/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading