diff --git a/stac_fastapi/pgstac/core.py b/stac_fastapi/pgstac/core.py index d159ba6..59cecb9 100644 --- a/stac_fastapi/pgstac/core.py +++ b/stac_fastapi/pgstac/core.py @@ -88,12 +88,6 @@ async def all_collections( # noqa: C901 **kwargs, ) - # NOTE: `FreeTextExtension` - pgstac will only accept `str` so we need to - # join the list[str] with ` OR ` - # ref: https://github.com/stac-utils/stac-fastapi-pgstac/pull/263 - if q := clean_args.pop("q", None): - clean_args["q"] = " OR ".join(q) if isinstance(q, list) else q - async with request.app.state.get_connection(request, "r") as conn: q, p = render( """ @@ -260,9 +254,11 @@ async def _search_base( # noqa: C901 search_request.conf = search_request.conf or {} search_request.conf["nohydrate"] = settings.use_api_hydrate - search_request_json = search_request.model_dump_json( - exclude_none=True, by_alias=True + search_request_json = search_request.model_dump( + exclude_none=True, by_alias=True, mode="json" ) + search_request_json = self._clean_search_args({}, **search_request_json) + search_request_json = orjson.dumps(search_request_json) try: async with request.app.state.get_connection(request, "r") as conn: @@ -613,10 +609,14 @@ def _clean_search_args( # noqa: C901 else: includes.add(field) - base_args["fields"] = {"include": includes, "exclude": excludes} + # ensure sets are converted to list to avoid JSON serialization issues + base_args["fields"] = {"include": list(includes), "exclude": list(excludes)} if q: - base_args["q"] = q + # NOTE: `FreeTextExtension` - pgstac will only accept `str` so we need to + # join the list[str] with ` OR ` + # ref: https://github.com/stac-utils/stac-fastapi-pgstac/pull/263 + base_args["q"] = " OR ".join(q) if isinstance(q, list) else q # Remove None values from dict clean = {} diff --git a/tests/resources/test_item.py b/tests/resources/test_item.py index 490d652..b4b009a 100644 --- a/tests/resources/test_item.py +++ b/tests/resources/test_item.py @@ -1701,15 +1701,64 @@ async def test_item_search_freetext(app_client, load_test_data, load_test_collec f"/collections/{test_item['collection']}/items", json=test_item ) assert resp.status_code == 201 + test_item2 = load_test_data("test_item2.json") + resp = await app_client.post( + f"/collections/{test_item['collection']}/items", json=test_item2 + ) + assert resp.status_code == 201 - # free-text - resp = await app_client.get( + # free-text basic (using POST) + resp = await app_client.post( "/search", - params={"q": "orthorectified"}, + json={"q": ["orthorectified"]}, + ) + assert resp.json()["numberReturned"] == 1 + assert resp.json()["features"][0]["id"] == "test-item" + + resp = await app_client.post( + "/search", + json={"q": ["orthorectified", "yo"]}, + ) + assert resp.json()["numberReturned"] == 1 + assert resp.json()["features"][0]["id"] == "test-item" + + resp = await app_client.post( + "/search", + json={"q": ["orthorectified", "Reflectance"]}, + ) + assert resp.json()["numberReturned"] == 2 + assert resp.json()["features"][0]["id"] == "test-item" + assert resp.json()["features"][1]["id"] == "test-item2" + + # free-text advanced (using POST) + resp = await app_client.post( + "/search", + json={"q": "orthorectified OR yo"}, ) assert resp.json()["numberReturned"] == 1 assert resp.json()["features"][0]["id"] == "test-item" + resp = await app_client.post( + "/search", + json={"q": "orthorectified OR Reflectance"}, + ) + assert resp.json()["numberReturned"] == 2 + assert resp.json()["features"][0]["id"] == "test-item" + assert resp.json()["features"][1]["id"] == "test-item2" + + resp = await app_client.post( + "/search", + json={"q": "orthorectified AND Reflectance"}, + ) + assert resp.json()["numberReturned"] == 0 + + # free-text basic (using GET) + resp = await app_client.get( + "/search", + params={"q": "yo"}, + ) + assert resp.json()["numberReturned"] == 0 + resp = await app_client.get( "/search", params={"q": "orthorectified,yo"}, @@ -1717,6 +1766,22 @@ async def test_item_search_freetext(app_client, load_test_data, load_test_collec assert resp.json()["numberReturned"] == 1 assert resp.json()["features"][0]["id"] == "test-item" + # free-text advanced (using GET) + resp = await app_client.get( + "/search", + params={"q": "orthorectified"}, + ) + assert resp.json()["numberReturned"] == 1 + assert resp.json()["features"][0]["id"] == "test-item" + + resp = await app_client.get( + "/search", + params={"q": "orthorectified OR Reflectance"}, + ) + assert resp.json()["numberReturned"] == 2 + assert resp.json()["features"][0]["id"] == "test-item" + assert resp.json()["features"][1]["id"] == "test-item2" + resp = await app_client.get( "/search", params={"q": "yo"},