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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Fixed

- Fixed "list index out of range" error when using BETWEEN operator in CQL2-text filters. [#521](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/521)

### Removed

### Updated
Expand Down
2 changes: 1 addition & 1 deletion stac_fastapi/core/stac_fastapi/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ async def post_search(
search = await self.database.apply_cql2_filter(search, cql2_filter)
except Exception as e:
raise HTTPException(
status_code=400, detail=f"Error with cql2_json filter: {e}"
status_code=400, detail=f"Error with cql2 filter: {e}"
)

if hasattr(search_request, "q"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,19 @@ def to_es(queryables_mapping: Dict[str, Any], query: Dict[str, Any]) -> Dict[str

elif query["op"] == AdvancedComparisonOp.BETWEEN:
field = to_es_field(queryables_mapping, query["args"][0]["property"])
gte, lte = query["args"][1], query["args"][2]

# Handle both formats: [property, [lower, upper]] or [property, lower, upper]
if len(query["args"]) == 2 and isinstance(query["args"][1], list):
# Format: [{'property': '...'}, [lower, upper]]
gte, lte = query["args"][1][0], query["args"][1][1]
elif len(query["args"]) == 3:
# Format: [{'property': '...'}, lower, upper]
gte, lte = query["args"][1], query["args"][2]
else:
raise ValueError(
f"BETWEEN operator expects 2 or 3 args, got {len(query['args'])}"
)

if isinstance(gte, dict) and "timestamp" in gte:
gte = gte["timestamp"]
if isinstance(lte, dict) and "timestamp" in lte:
Expand Down
20 changes: 19 additions & 1 deletion stac_fastapi/tests/extensions/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ async def test_search_filter_extension_in_no_list(app_client, ctx):

assert resp.status_code == 400
assert resp.json() == {
"detail": f"Error with cql2_json filter: Arg {product_id} is not a list"
"detail": f"Error with cql2 filter: Arg {product_id} is not a list"
}


Expand Down Expand Up @@ -440,6 +440,24 @@ async def test_search_filter_extension_between(app_client, ctx):
assert len(resp.json()["features"]) == 1


@pytest.mark.asyncio
async def test_search_filter_extension_between_get(app_client, ctx):
"""Test BETWEEN operator with GET request using CQL2-text format."""
sun_elevation = ctx.item["properties"]["view:sun_elevation"]
lower_bound = sun_elevation - 0.01
upper_bound = sun_elevation + 0.01

# Use CQL2-text format for GET request
filter_expr = f"properties.view:sun_elevation BETWEEN {lower_bound} AND {upper_bound} AND id = '{ctx.item['id']}'"

resp = await app_client.get(
"/search", params={"filter": filter_expr, "filter_lang": "cql2-text"}
)

assert resp.status_code == 200
assert len(resp.json()["features"]) == 1


@pytest.mark.asyncio
async def test_search_filter_extension_isnull_post(app_client, ctx):
# Test for a property that is not null
Expand Down