Skip to content
Merged
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
6 changes: 6 additions & 0 deletions haystack/utils/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ 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"Unknown logical operator '{operator}'. Valid operators are: {sorted(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 +217,8 @@ 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"Unknown comparison operator '{operator}'. Valid operators are: {sorted(COMPARISON_OPERATORS)}"
raise FilterError(msg)
filter_value: Any = condition["value"]
return COMPARISON_OPERATORS[operator](filter_value=filter_value, value=document_value)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
fixes:
- |
Metadata filters that use an unsupported operator now raise a clear ``FilterError``
that lists the valid operators, instead of a cryptic ``KeyError``. For example, a
typo such as ``{"field": "meta.year", "operator": "gt", "value": 2000}`` (instead of
``">"``) previously surfaced as ``KeyError: 'gt'``. This applies to both comparison
operators and logical operators (``AND``, ``OR``, ``NOT``).
52 changes: 52 additions & 0 deletions test/utils/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,23 @@ def test_document_matches_filter(filters, document, expected_result):
pytest.param({"operator": "==", "value": "test"}, id="Missing condition field key"),
pytest.param({"field": "meta.name", "value": "test"}, id="Missing condition operator key"),
pytest.param({"field": "meta.name", "operator": "=="}, id="Missing condition value key"),
# Unknown operators
pytest.param({"field": "meta.page", "operator": "gt", "value": 10}, id="Unknown comparison operator"),
pytest.param(
{"operator": "XOR", "conditions": [{"field": "meta.page", "operator": "==", "value": 10}]},
id="Unknown logical operator",
),
pytest.param(
{
"operator": "AND",
"conditions": [{"operator": "XOR", "conditions": [{"field": "meta.page", "operator": "==", "value": 10}]}],
},
id="Unknown nested logical operator",
),
pytest.param(
{"operator": "and", "conditions": [{"field": "meta.page", "operator": "==", "value": 10}]},
id="Lowercase logical operator",
),
]


Expand All @@ -574,3 +591,38 @@ 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,expected_message",
[
pytest.param(
{"field": "meta.page", "operator": "gt", "value": 10},
"Unknown comparison operator 'gt'",
id="Unknown comparison operator",
),
pytest.param(
{"operator": "XOR", "conditions": [{"field": "meta.page", "operator": "==", "value": 10}]},
"Unknown logical operator 'XOR'",
id="Unknown logical operator",
),
pytest.param(
{
"operator": "AND",
"conditions": [
{"operator": "XOR", "conditions": [{"field": "meta.page", "operator": "==", "value": 10}]}
],
},
"Unknown logical operator 'XOR'",
id="Unknown nested logical operator",
),
pytest.param(
{"operator": "and", "conditions": [{"field": "meta.page", "operator": "==", "value": 10}]},
"Unknown logical operator 'and'",
id="Lowercase logical operator",
),
],
)
def test_document_matches_filter_unknown_operator_error_message(filters, expected_message):
with pytest.raises(FilterError, match=expected_message):
document_matches_filter(filters, Document(meta={"page": 10}))
Loading