Skip to content

Commit ab7d930

Browse files
authored
Merge pull request #157 from stac-utils/fix-get-sort
Fix GET sortby, tests
2 parents 8b12370 + b5ec2b0 commit ab7d930

File tree

5 files changed

+87
-57
lines changed

5 files changed

+87
-57
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2727
- Corrected the automatic converstion of float values to int when building Filter Clauses [#135](https://github.com/stac-utils/stac-fastapi-elasticsearch/issues/135)
2828
- Do not index `proj:geometry` field as geo_shape [#154](https://github.com/stac-utils/stac-fastapi-elasticsearch/issues/154)
2929
- Remove unsupported characters from Elasticsearch index names [#153](https://github.com/stac-utils/stac-fastapi-elasticsearch/issues/153)
30+
- Fixed GET /search sortby requests [#25](https://github.com/stac-utils/stac-fastapi-elasticsearch/issues/25)
3031

3132
## [v0.3.0]
3233

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/core.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,10 @@ async def get_search(
372372
sort_param.append(
373373
{
374374
"field": sort[1:],
375-
"direction": "asc" if sort[0] == "+" else "desc",
375+
"direction": "desc" if sort[0] == "-" else "asc",
376376
}
377377
)
378+
print(sort_param)
378379
base_args["sortby"] = sort_param
379380

380381
if filter:

stac_fastapi/elasticsearch/tests/api/test_api.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,77 @@ async def test_app_query_extension_limit_10000(app_client):
218218

219219

220220
@pytest.mark.asyncio
221-
async def test_app_sort_extension(app_client, txn_client, ctx):
221+
async def test_app_sort_extension_get_asc(app_client, txn_client, ctx):
222+
first_item = ctx.item
223+
item_date = datetime.strptime(
224+
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"
225+
)
226+
227+
second_item = dict(first_item)
228+
second_item["id"] = "another-item"
229+
another_item_date = item_date - timedelta(days=1)
230+
second_item["properties"]["datetime"] = another_item_date.strftime(
231+
"%Y-%m-%dT%H:%M:%SZ"
232+
)
233+
await create_item(txn_client, second_item)
234+
235+
resp = await app_client.get("/search?sortby=+properties.datetime")
236+
assert resp.status_code == 200
237+
resp_json = resp.json()
238+
assert resp_json["features"][1]["id"] == first_item["id"]
239+
assert resp_json["features"][0]["id"] == second_item["id"]
240+
241+
242+
@pytest.mark.asyncio
243+
async def test_app_sort_extension_get_desc(app_client, txn_client, ctx):
244+
first_item = ctx.item
245+
item_date = datetime.strptime(
246+
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"
247+
)
248+
249+
second_item = dict(first_item)
250+
second_item["id"] = "another-item"
251+
another_item_date = item_date - timedelta(days=1)
252+
second_item["properties"]["datetime"] = another_item_date.strftime(
253+
"%Y-%m-%dT%H:%M:%SZ"
254+
)
255+
await create_item(txn_client, second_item)
256+
257+
resp = await app_client.get("/search?sortby=-properties.datetime")
258+
assert resp.status_code == 200
259+
resp_json = resp.json()
260+
assert resp_json["features"][0]["id"] == first_item["id"]
261+
assert resp_json["features"][1]["id"] == second_item["id"]
262+
263+
264+
@pytest.mark.asyncio
265+
async def test_app_sort_extension_post_asc(app_client, txn_client, ctx):
266+
first_item = ctx.item
267+
item_date = datetime.strptime(
268+
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"
269+
)
270+
271+
second_item = dict(first_item)
272+
second_item["id"] = "another-item"
273+
another_item_date = item_date - timedelta(days=1)
274+
second_item["properties"]["datetime"] = another_item_date.strftime(
275+
"%Y-%m-%dT%H:%M:%SZ"
276+
)
277+
await create_item(txn_client, second_item)
278+
279+
params = {
280+
"collections": [first_item["collection"]],
281+
"sortby": [{"field": "properties.datetime", "direction": "asc"}],
282+
}
283+
resp = await app_client.post("/search", json=params)
284+
assert resp.status_code == 200
285+
resp_json = resp.json()
286+
assert resp_json["features"][1]["id"] == first_item["id"]
287+
assert resp_json["features"][0]["id"] == second_item["id"]
288+
289+
290+
@pytest.mark.asyncio
291+
async def test_app_sort_extension_post_desc(app_client, txn_client, ctx):
222292
first_item = ctx.item
223293
item_date = datetime.strptime(
224294
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"

stac_fastapi/elasticsearch/tests/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ class Config:
6464

6565
@pytest.fixture(scope="session")
6666
def event_loop():
67-
return asyncio.get_event_loop()
67+
loop = asyncio.new_event_loop()
68+
yield loop
69+
loop.close()
6870

6971

7072
def _load_file(filename: str) -> Dict:

stac_fastapi/elasticsearch/tests/resources/test_item.py

Lines changed: 10 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ async def test_item_collection_filter_datetime(app_client, ctx):
258258
assert len(resp_json["features"]) == 0
259259

260260

261+
@pytest.mark.asyncio
261262
@pytest.mark.skip(reason="Pagination extension not implemented")
262263
async def test_pagination(app_client, load_test_data):
263264
"""Test item collection pagination (paging extension)"""
@@ -384,6 +385,7 @@ async def test_item_search_temporal_window_post(app_client, ctx):
384385
assert resp_json["features"][0]["id"] == test_item["id"]
385386

386387

388+
@pytest.mark.asyncio
387389
@pytest.mark.skip(reason="KeyError: 'features")
388390
async def test_item_search_temporal_open_window(app_client, ctx):
389391
"""Test POST search with open spatio-temporal query (core)"""
@@ -398,39 +400,6 @@ async def test_item_search_temporal_open_window(app_client, ctx):
398400
assert resp_json["features"][0]["id"] == test_item["id"]
399401

400402

401-
@pytest.mark.skip(reason="sortby date not implemented")
402-
async def test_item_search_sort_post(app_client, load_test_data):
403-
"""Test POST search with sorting (sort extension)"""
404-
first_item = load_test_data("test_item.json")
405-
item_date = rfc3339_str_to_datetime(first_item["properties"]["datetime"])
406-
resp = await app_client.post(
407-
f"/collections/{first_item['collection']}/items", json=first_item
408-
)
409-
assert resp.status_code == 200
410-
411-
second_item = load_test_data("test_item.json")
412-
second_item["id"] = "another-item"
413-
another_item_date = item_date - timedelta(days=1)
414-
second_item["properties"]["datetime"] = datetime_to_str(another_item_date)
415-
resp = await app_client.post(
416-
f"/collections/{second_item['collection']}/items", json=second_item
417-
)
418-
assert resp.status_code == 200
419-
420-
params = {
421-
"collections": [first_item["collection"]],
422-
"sortby": [{"field": "datetime", "direction": "desc"}],
423-
}
424-
resp = await app_client.post("/search", json=params)
425-
assert resp.status_code == 200
426-
resp_json = resp.json()
427-
assert resp_json["features"][0]["id"] == first_item["id"]
428-
assert resp_json["features"][1]["id"] == second_item["id"]
429-
await app_client.delete(
430-
f"/collections/{first_item['collection']}/items/{first_item['id']}"
431-
)
432-
433-
434403
@pytest.mark.asyncio
435404
async def test_item_search_by_id_get(app_client, ctx, txn_client):
436405
"""Test GET search by item id (core)"""
@@ -498,27 +467,6 @@ async def test_item_search_temporal_window_get(app_client, ctx):
498467
assert resp_json["features"][0]["id"] == test_item["id"]
499468

500469

501-
@pytest.mark.skip(reason="sorting not fully implemented")
502-
async def test_item_search_sort_get(app_client, ctx, txn_client):
503-
"""Test GET search with sorting (sort extension)"""
504-
first_item = ctx.item
505-
item_date = rfc3339_str_to_datetime(first_item["properties"]["datetime"])
506-
await create_item(txn_client, ctx.item)
507-
508-
second_item = ctx.item.copy()
509-
second_item["id"] = "another-item"
510-
another_item_date = item_date - timedelta(days=1)
511-
second_item.update({"properties": {"datetime": datetime_to_str(another_item_date)}})
512-
await create_item(txn_client, second_item)
513-
514-
params = {"collections": [first_item["collection"]], "sortby": "-datetime"}
515-
resp = await app_client.get("/search", params=params)
516-
assert resp.status_code == 200
517-
resp_json = resp.json()
518-
assert resp_json["features"][0]["id"] == first_item["id"]
519-
assert resp_json["features"][1]["id"] == second_item["id"]
520-
521-
522470
@pytest.mark.asyncio
523471
async def test_item_search_post_without_collection(app_client, ctx):
524472
"""Test POST search without specifying a collection"""
@@ -742,6 +690,7 @@ async def test_field_extension_post(app_client, ctx):
742690
}
743691

744692

693+
@pytest.mark.asyncio
745694
async def test_field_extension_exclude_and_include(app_client, ctx):
746695
"""Test POST search including/excluding same field (fields extension)"""
747696
test_item = ctx.item
@@ -758,6 +707,7 @@ async def test_field_extension_exclude_and_include(app_client, ctx):
758707
assert "eo:cloud_cover" not in resp_json["features"][0]["properties"]
759708

760709

710+
@pytest.mark.asyncio
761711
async def test_field_extension_exclude_default_includes(app_client, ctx):
762712
"""Test POST search excluding a forbidden field (fields extension)"""
763713
test_item = ctx.item
@@ -768,6 +718,7 @@ async def test_field_extension_exclude_default_includes(app_client, ctx):
768718
assert "gsd" not in resp_json["features"][0]
769719

770720

721+
@pytest.mark.asyncio
771722
async def test_search_intersects_and_bbox(app_client):
772723
"""Test POST search intersects and bbox are mutually exclusive (core)"""
773724
bbox = [-118, 34, -117, 35]
@@ -777,20 +728,23 @@ async def test_search_intersects_and_bbox(app_client):
777728
assert resp.status_code == 400
778729

779730

731+
@pytest.mark.asyncio
780732
async def test_get_missing_item(app_client, load_test_data):
781733
"""Test read item which does not exist (transactions extension)"""
782734
test_coll = load_test_data("test_collection.json")
783735
resp = await app_client.get(f"/collections/{test_coll['id']}/items/invalid-item")
784736
assert resp.status_code == 404
785737

786738

739+
@pytest.mark.asyncio
787740
@pytest.mark.skip(reason="invalid queries not implemented")
788741
async def test_search_invalid_query_field(app_client):
789742
body = {"query": {"gsd": {"lt": 100}, "invalid-field": {"eq": 50}}}
790743
resp = await app_client.post("/search", json=body)
791744
assert resp.status_code == 400
792745

793746

747+
@pytest.mark.asyncio
794748
async def test_search_bbox_errors(app_client):
795749
body = {"query": {"bbox": [0]}}
796750
resp = await app_client.post("/search", json=body)
@@ -805,6 +759,7 @@ async def test_search_bbox_errors(app_client):
805759
assert resp.status_code == 400
806760

807761

762+
@pytest.mark.asyncio
808763
async def test_conformance_classes_configurable():
809764
"""Test conformance class configurability"""
810765
landing = LandingPageMixin()
@@ -822,6 +777,7 @@ async def test_conformance_classes_configurable():
822777
assert client.conformance_classes()[0] == "this is a test"
823778

824779

780+
@pytest.mark.asyncio
825781
async def test_search_datetime_validation_errors(app_client):
826782
bad_datetimes = [
827783
"37-01-01T12:00:27.87Z",

0 commit comments

Comments
 (0)