Skip to content

Commit c89360c

Browse files
fix filter extension implementation (#149)
1 parent c7b17e6 commit c89360c

File tree

4 files changed

+130
-11
lines changed

4 files changed

+130
-11
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- Fix Docker compose file, so example data can be loaded into database (author @zstatmanweil, https://github.com/stac-utils/stac-fastapi-pgstac/pull/142)
66

7+
- Fix `filter` extension implementation in `CoreCrudClient`
8+
79
## [3.0.0] - 2024-08-02
810

911
- Enable filter extension for `GET /items` requests and add `Queryables` links in `/collections` and `/collections/{collection_id}` responses ([#89](https://github.com/stac-utils/stac-fastapi-pgstac/pull/89))

docker-compose.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3'
21
services:
32
app:
43
container_name: stac-fastapi-pgstac
@@ -67,8 +66,8 @@ services:
6766
- ./scripts:/app/scripts
6867
command: >
6968
/bin/sh -c "
70-
./scripts/wait-for-it.sh -t 60 app:8082 &&
71-
python -m pip install requests &&
69+
./scripts/wait-for-it.sh -t 60 app:8082 &&
70+
python -m pip install requests &&
7271
python /app/scripts/ingest_joplin.py http://app:8082
7372
"
7473
depends_on:

stac_fastapi/pgstac/core.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,14 @@ async def item_collection(
310310

311311
if self.extension_is_enabled("FilterExtension"):
312312
filter_lang = kwargs.get("filter_lang", None)
313-
filter = kwargs.get("filter", None)
314-
if filter is not None and filter_lang == "cql2-text":
315-
ast = parse_cql2_text(filter.strip())
316-
base_args["filter"] = orjson.loads(to_cql2(ast))
317-
base_args["filter-lang"] = "cql2-json"
313+
filter_query = kwargs.get("filter", None)
314+
if filter_query:
315+
if filter_lang == "cql2-text":
316+
filter_query = to_cql2(parse_cql2_text(filter_query))
317+
filter_lang = "cql2-json"
318+
319+
base_args["filter"] = orjson.loads(filter_query)
320+
base_args["filter-lang"] = filter_lang
318321

319322
clean = {}
320323
for k, v in base_args.items():
@@ -420,9 +423,11 @@ async def get_search( # noqa: C901
420423

421424
if filter:
422425
if filter_lang == "cql2-text":
423-
ast = parse_cql2_text(filter)
424-
base_args["filter"] = orjson.loads(to_cql2(ast))
425-
base_args["filter-lang"] = "cql2-json"
426+
filter = to_cql2(parse_cql2_text(filter))
427+
filter_lang = "cql2-json"
428+
429+
base_args["filter"] = orjson.loads(filter)
430+
base_args["filter-lang"] = filter_lang
426431

427432
if datetime:
428433
base_args["datetime"] = format_datetime_range(datetime)

tests/resources/test_item.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,3 +1540,116 @@ async def test_get_collection_items_duplicate_forwarded_headers(
15401540
)
15411541
for link in resp.json()["features"][0]["links"]:
15421542
assert link["href"].startswith("https://test:1234/")
1543+
1544+
1545+
async def test_get_filter_extension(app_client, load_test_data, load_test_collection):
1546+
"""Test GET with Filter extension"""
1547+
test_item = load_test_data("test_item.json")
1548+
collection_id = test_item["collection"]
1549+
ids = []
1550+
1551+
# Ingest 5 items
1552+
for _ in range(5):
1553+
uid = str(uuid.uuid4())
1554+
test_item["id"] = uid
1555+
resp = await app_client.post(
1556+
f"/collections/{collection_id}/items", json=test_item
1557+
)
1558+
assert resp.status_code == 201
1559+
ids.append(uid)
1560+
1561+
search_id = ids[2]
1562+
1563+
# SEARCH
1564+
# CQL2-JSON
1565+
resp = await app_client.get(
1566+
"/search",
1567+
params={
1568+
"filter-lang": "cql2-json",
1569+
"filter": json.dumps({"op": "in", "args": [{"property": "id"}, [search_id]]}),
1570+
},
1571+
)
1572+
assert resp.status_code == 200
1573+
fc = resp.json()
1574+
assert len(fc["features"]) == 1
1575+
assert fc["features"][0]["id"] == search_id
1576+
1577+
# CQL-JSON
1578+
resp = await app_client.get(
1579+
"/search",
1580+
params={
1581+
"filter-lang": "cql-json",
1582+
"filter": json.dumps(
1583+
{
1584+
"eq": [
1585+
{"property": "id"},
1586+
search_id,
1587+
],
1588+
},
1589+
),
1590+
},
1591+
)
1592+
assert resp.status_code == 200
1593+
fc = resp.json()
1594+
assert len(fc["features"]) == 1
1595+
assert fc["features"][0]["id"] == search_id
1596+
1597+
# CQL2-TEXT
1598+
resp = await app_client.get(
1599+
"/search",
1600+
params={
1601+
"filter-lang": "cql2-text",
1602+
"filter": f"id='{search_id}'",
1603+
},
1604+
)
1605+
assert resp.status_code == 200
1606+
fc = resp.json()
1607+
assert len(fc["features"]) == 1
1608+
assert fc["features"][0]["id"] == search_id
1609+
1610+
# ITEM COLLECTION
1611+
# CQL2-JSON
1612+
resp = await app_client.get(
1613+
f"/collections/{collection_id}/items",
1614+
params={
1615+
"filter-lang": "cql2-json",
1616+
"filter": json.dumps({"op": "in", "args": [{"property": "id"}, [search_id]]}),
1617+
},
1618+
)
1619+
assert resp.status_code == 200
1620+
fc = resp.json()
1621+
assert len(fc["features"]) == 1
1622+
assert fc["features"][0]["id"] == search_id
1623+
1624+
# CQL-JSON
1625+
resp = await app_client.get(
1626+
f"/collections/{collection_id}/items",
1627+
params={
1628+
"filter-lang": "cql-json",
1629+
"filter": json.dumps(
1630+
{
1631+
"eq": [
1632+
{"property": "id"},
1633+
search_id,
1634+
],
1635+
},
1636+
),
1637+
},
1638+
)
1639+
assert resp.status_code == 200
1640+
fc = resp.json()
1641+
assert len(fc["features"]) == 1
1642+
assert fc["features"][0]["id"] == search_id
1643+
1644+
# CQL2-TEXT
1645+
resp = await app_client.get(
1646+
f"/collections/{collection_id}/items",
1647+
params={
1648+
"filter-lang": "cql2-text",
1649+
"filter": f"id='{search_id}'",
1650+
},
1651+
)
1652+
assert resp.status_code == 200
1653+
fc = resp.json()
1654+
assert len(fc["features"]) == 1
1655+
assert fc["features"][0]["id"] == search_id

0 commit comments

Comments
 (0)