Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def _group_nested_conditions(
inner.append(_parse_logical_condition(c, nested_fields=None))
else:
inner.append(_parse_comparison_condition(c, nested_fields))
if len(inner) > 1:
if len(inner) > 1 and operator != "OR":
inner = _normalize_ranges(inner)
if len(inner) == 1:
conditions.append({"nested": {"path": path, "query": inner[0]}})
Expand All @@ -120,7 +120,10 @@ def _parse_logical_condition(condition: dict[str, Any], nested_fields: set[str]
else:
conditions = [_parse_comparison_condition(c, nested_fields) for c in condition["conditions"]]

if len(conditions) > 1:
# Range merging is only valid when the conditions are AND-combined (AND, and the
# inner `must` of NOT). Merging same-field ranges under OR would collapse e.g.
# `price < 10 OR price > 100` into a single impossible range, matching nothing.
if len(conditions) > 1 and operator != "OR":
conditions = _normalize_ranges(conditions)
if operator == "AND":
return {"bool": {"must": conditions}}
Expand Down
53 changes: 53 additions & 0 deletions integrations/opensearch/tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,59 @@ def test_normalize_ranges():
]


def test_or_of_ranges_on_same_field_are_not_merged():
# `price < 10 OR price > 100` must stay two separate range clauses under `should`.
# Merging them into a single {"lt": 10, "gt": 100} range means price < 10 AND
# price > 100, which matches no document.
filters = {
"operator": "OR",
"conditions": [
{"field": "meta.price", "operator": "<", "value": 10},
{"field": "meta.price", "operator": ">", "value": 100},
],
}
assert normalize_filters(filters) == {
"bool": {
"should": [
{"range": {"price": {"lt": 10}}},
{"range": {"price": {"gt": 100}}},
]
}
}


def test_or_of_ranges_on_same_nested_field_are_not_merged():
# Same range-merge bug inside a nested-field logical group: an OR of two ranges on
# the same nested sub-field must stay two separate range clauses, not collapse into
# a single impossible range.
filters = {
"operator": "OR",
"conditions": [
{"field": "meta.refs.score", "operator": "<", "value": 10},
{"field": "meta.refs.score", "operator": ">", "value": 100},
],
}
assert normalize_filters(filters, nested_fields={"refs"}) == {
"bool": {
"should": [
{
"nested": {
"path": "refs",
"query": {
"bool": {
"should": [
{"range": {"refs.score": {"lt": 10}}},
{"range": {"refs.score": {"gt": 100}}},
]
}
},
}
}
]
}
}


@pytest.mark.parametrize("filters, nested_fields, expected", nested_filters_data)
def test_normalize_filters_with_nested_fields(filters, nested_fields, expected):
result = normalize_filters(filters, nested_fields=nested_fields)
Expand Down
Loading