Skip to content

Commit cd1f3a1

Browse files
Merge branch 'main' into add-opensearch
2 parents 61e563a + 481f61e commit cd1f3a1

File tree

7 files changed

+160
-15
lines changed

7 files changed

+160
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1515
### Changed
1616

1717
- Elasticsearch drivers from 7.17.9 to 8.11.0 [#169](https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/169)
18+
- Collection update endpoint no longer delete all sub items [#177](https://github.com/stac-utils/stac-fastapi-elasticsearch/pull/177)
1819

1920
### Fixed
2021

docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ services:
1414
- RELOAD=true
1515
- ENVIRONMENT=local
1616
- WEB_CONCURRENCY=10
17-
- ES_HOST=172.17.0.1
17+
- ES_HOST=elasticsearch
1818
- ES_PORT=9200
1919
- ES_USE_SSL=false
2020
- ES_VERIFY_CERTS=false
@@ -62,6 +62,7 @@ services:
6262
elasticsearch:
6363
container_name: es-container
6464
image: docker.elastic.co/elasticsearch/elasticsearch:${ELASTICSEARCH_VERSION:-8.11.0}
65+
hostname: elasticsearch
6566
environment:
6667
ES_JAVA_OPTS: -Xms512m -Xmx1g
6768
volumes:

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/config/config_elasticsearch.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ def _es_config() -> Dict[str, Any]:
3939
if (u := os.getenv("ES_USER")) and (p := os.getenv("ES_PASS")):
4040
config["http_auth"] = (u, p)
4141

42+
if api_key := os.getenv("ES_API_KEY"):
43+
if isinstance(config["headers"], dict):
44+
headers = {**config["headers"], "x-api-key": api_key}
45+
46+
else:
47+
config["headers"] = {"x-api-key": api_key}
48+
49+
config["headers"] = headers
50+
4251
return config
4352

4453

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/core.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,9 @@ async def post_search(
468468
)
469469

470470
if search_request.query:
471-
for (field_name, expr) in search_request.query.items():
471+
for field_name, expr in search_request.query.items():
472472
field = "properties__" + field_name
473-
for (op, value) in expr.items():
473+
for op, value in expr.items():
474474
search = self.database.apply_stacql_filter(
475475
search=search, op=op, field=field, value=value
476476
)
@@ -671,8 +671,11 @@ async def update_collection(
671671
Update a collection.
672672
673673
This method updates an existing collection in the database by first finding
674-
the collection by its id, then deleting the old version, and finally creating
675-
a new version of the updated collection. The updated collection is then returned.
674+
the collection by the id given in the keyword argument `collection_id`.
675+
If no `collection_id` is given the id of the given collection object is used.
676+
If the object and keyword collection ids don't match the sub items
677+
collection id is updated else the items are left unchanged.
678+
The updated collection is then returned.
676679
677680
Args:
678681
collection: A STAC collection that needs to be updated.
@@ -684,9 +687,18 @@ async def update_collection(
684687
"""
685688
base_url = str(kwargs["request"].base_url)
686689

687-
await self.database.find_collection(collection_id=collection["id"])
688-
await self.delete_collection(collection["id"])
689-
await self.create_collection(collection, **kwargs)
690+
collection_id = kwargs["request"].query_params.get(
691+
"collection_id", collection["id"]
692+
)
693+
694+
collection_links = CollectionLinks(
695+
collection_id=collection["id"], base_url=base_url
696+
).create_links()
697+
collection["links"] = collection_links
698+
699+
await self.database.update_collection(
700+
collection_id=collection_id, collection=collection
701+
)
690702

691703
return CollectionSerializer.db_to_stac(collection, base_url)
692704

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic/database_logic_elasticsearch.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,53 @@ async def find_collection(self, collection_id: str) -> Collection:
749749

750750
return collection["_source"]
751751

752+
async def update_collection(
753+
self, collection_id: str, collection: Collection, refresh: bool = False
754+
):
755+
"""Update a collection from the database.
756+
757+
Args:
758+
self: The instance of the object calling this function.
759+
collection_id (str): The ID of the collection to be updated.
760+
collection (Collection): The Collection object to be used for the update.
761+
762+
Raises:
763+
NotFoundError: If the collection with the given `collection_id` is not
764+
found in the database.
765+
766+
Notes:
767+
This function updates the collection in the database using the specified
768+
`collection_id` and with the collection specified in the `Collection` object.
769+
If the collection is not found, a `NotFoundError` is raised.
770+
"""
771+
await self.find_collection(collection_id=collection_id)
772+
773+
if collection_id != collection["id"]:
774+
await self.create_collection(collection, refresh=refresh)
775+
776+
await self.client.reindex(
777+
body={
778+
"dest": {"index": f"{ITEMS_INDEX_PREFIX}{collection['id']}"},
779+
"source": {"index": f"{ITEMS_INDEX_PREFIX}{collection_id}"},
780+
"script": {
781+
"lang": "painless",
782+
"source": f"""ctx._id = ctx._id.replace('{collection_id}', '{collection["id"]}'); ctx._source.collection = '{collection["id"]}' ;""",
783+
},
784+
},
785+
wait_for_completion=True,
786+
refresh=refresh,
787+
)
788+
789+
await self.delete_collection(collection_id)
790+
791+
else:
792+
await self.client.index(
793+
index=COLLECTIONS_INDEX,
794+
id=collection_id,
795+
document=collection,
796+
refresh=refresh,
797+
)
798+
752799
async def delete_collection(self, collection_id: str, refresh: bool = False):
753800
"""Delete a collection from the database.
754801

stac_fastapi/elasticsearch/tests/clients/test_elasticsearch.py

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,90 @@ async def test_update_collection(
4040
txn_client,
4141
load_test_data: Callable,
4242
):
43-
data = load_test_data("test_collection.json")
43+
collection_data = load_test_data("test_collection.json")
44+
item_data = load_test_data("test_item.json")
4445

45-
await txn_client.create_collection(data, request=MockRequest)
46-
data["keywords"].append("new keyword")
47-
await txn_client.update_collection(data, request=MockRequest)
46+
await txn_client.create_collection(collection_data, request=MockRequest)
47+
await txn_client.create_item(
48+
collection_id=collection_data["id"],
49+
item=item_data,
50+
request=MockRequest,
51+
refresh=True,
52+
)
4853

49-
coll = await core_client.get_collection(data["id"], request=MockRequest)
54+
collection_data["keywords"].append("new keyword")
55+
await txn_client.update_collection(collection_data, request=MockRequest)
56+
57+
coll = await core_client.get_collection(collection_data["id"], request=MockRequest)
5058
assert "new keyword" in coll["keywords"]
5159

52-
await txn_client.delete_collection(data["id"])
60+
item = await core_client.get_item(
61+
item_id=item_data["id"],
62+
collection_id=collection_data["id"],
63+
request=MockRequest,
64+
)
65+
assert item["id"] == item_data["id"]
66+
assert item["collection"] == item_data["collection"]
67+
68+
await txn_client.delete_collection(collection_data["id"])
69+
70+
71+
@pytest.mark.asyncio
72+
async def test_update_collection_id(
73+
core_client,
74+
txn_client,
75+
load_test_data: Callable,
76+
):
77+
collection_data = load_test_data("test_collection.json")
78+
item_data = load_test_data("test_item.json")
79+
new_collection_id = "new-test-collection"
80+
81+
await txn_client.create_collection(collection_data, request=MockRequest)
82+
await txn_client.create_item(
83+
collection_id=collection_data["id"],
84+
item=item_data,
85+
request=MockRequest,
86+
refresh=True,
87+
)
88+
89+
old_collection_id = collection_data["id"]
90+
collection_data["id"] = new_collection_id
91+
92+
await txn_client.update_collection(
93+
collection=collection_data,
94+
request=MockRequest(
95+
query_params={
96+
"collection_id": old_collection_id,
97+
"limit": "10",
98+
}
99+
),
100+
refresh=True,
101+
)
102+
103+
with pytest.raises(NotFoundError):
104+
await core_client.get_collection(old_collection_id, request=MockRequest)
105+
106+
coll = await core_client.get_collection(collection_data["id"], request=MockRequest)
107+
assert coll["id"] == new_collection_id
108+
109+
with pytest.raises(NotFoundError):
110+
await core_client.get_item(
111+
item_id=item_data["id"],
112+
collection_id=old_collection_id,
113+
request=MockRequest,
114+
)
115+
116+
item = await core_client.get_item(
117+
item_id=item_data["id"],
118+
collection_id=collection_data["id"],
119+
request=MockRequest,
120+
refresh=True,
121+
)
122+
123+
assert item["id"] == item_data["id"]
124+
assert item["collection"] == new_collection_id
125+
126+
await txn_client.delete_collection(collection_data["id"])
53127

54128

55129
@pytest.mark.asyncio

stac_fastapi/elasticsearch/tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def __init__(self, item, collection):
5151

5252
class MockRequest:
5353
base_url = "http://test-server"
54+
query_params = {}
5455

5556
def __init__(
5657
self,
@@ -62,7 +63,7 @@ def __init__(
6263
self.method = method
6364
self.url = url
6465
self.app = app
65-
self.query_params = query_params or {}
66+
self.query_params = query_params
6667

6768

6869
class TestSettings(AsyncSearchSettings):

0 commit comments

Comments
 (0)